Merge lp:~mardy/account-polld/lp1493733 into lp:~ubuntu-push-hackers/account-polld/trunk
- lp1493733
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Jonas G. Drange | ||||
Approved revision: | 140 | ||||
Merged at revision: | 141 | ||||
Proposed branch: | lp:~mardy/account-polld/lp1493733 | ||||
Merge into: | lp:~ubuntu-push-hackers/account-polld/trunk | ||||
Diff against target: |
1374 lines (+16/-1296) 6 files modified
cmd/account-polld/main.go (+0/-6) debian/account-polld.conf (+1/-1) debian/changelog (+14/-0) debian/control (+1/-1) plugins/facebook/facebook.go (+0/-453) plugins/facebook/facebook_test.go (+0/-835) |
||||
To merge this branch: | bzr merge lp:~mardy/account-polld/lp1493733 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Approve | |
Jonas G. Drange (community) | Needs Information | ||
Review via email: mp+270503@code.launchpad.net |
Commit message
Remove non working Facebook plugin
Facebook removed the possibility to retrieve notifications, effective from October 6, 2015. From https:/
"The GET /v2.4/{
and
"From October 6, 2015 onwards, in all previous API versions, these endpoints will return empty arrays"
This change contains a couple of packaging changes:
* debian/control, debian/
Remove mentions of Facebook from the descriptions.
Description of the change
Remove non working Facebook plugin
Facebook removed the possibility to retrieve notifications, effective from October 6, 2015. From https:/
"The GET /v2.4/{
and
"From October 6, 2015 onwards, in all previous API versions, these endpoints will return empty arrays"
PS Jenkins bot (ps-jenkins) wrote : | # |
- 139. By Alberto Mardegan
-
Sync changelog from archive
No-change test rebuild for g++5 ABI transition
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:139
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Jonas G. Drange (jonas-drange) wrote : | # |
Could you make a note of why the files under debian/ changed, as I think it's required for landing. Thanks!
- 140. By Alberto Mardegan
-
Update changelog
* Remove non-working facebook integration. (LP: #1493733)
* debian/control, debian/account- polld.conf:
Remove mentions of Facebook from the descriptions.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:140
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'cmd/account-polld/main.go' |
2 | --- cmd/account-polld/main.go 2015-03-20 17:51:02 +0000 |
3 | +++ cmd/account-polld/main.go 2015-10-12 13:54:21 +0000 |
4 | @@ -27,7 +27,6 @@ |
5 | "launchpad.net/account-polld/accounts" |
6 | "launchpad.net/account-polld/gettext" |
7 | "launchpad.net/account-polld/plugins" |
8 | - "launchpad.net/account-polld/plugins/facebook" |
9 | "launchpad.net/account-polld/plugins/gmail" |
10 | "launchpad.net/account-polld/plugins/twitter" |
11 | "launchpad.net/account-polld/pollbus" |
12 | @@ -47,7 +46,6 @@ |
13 | |
14 | SERVICENAME_GMAIL = "com.ubuntu.developer.webapps.webapp-gmail_webapp-gmail" |
15 | SERVICENAME_TWITTER = "com.ubuntu.developer.webapps.webapp-twitter_webapp-twitter" |
16 | - SERVICENAME_FACEBOOK = "com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook" |
17 | ) |
18 | |
19 | const ( |
20 | @@ -119,10 +117,6 @@ |
21 | case SERVICENAME_GMAIL: |
22 | log.Println("Creating account with id", data.AccountId, "for", data.ServiceName) |
23 | plugin = gmail.New(data.AccountId) |
24 | - case SERVICENAME_FACEBOOK: |
25 | - // This is just stubbed until the plugin exists. |
26 | - log.Println("Creating account with id", data.AccountId, "for", data.ServiceName) |
27 | - plugin = facebook.New(data.AccountId) |
28 | case SERVICENAME_TWITTER: |
29 | // This is just stubbed until the plugin exists. |
30 | log.Println("Creating account with id", data.AccountId, "for", data.ServiceName) |
31 | |
32 | === modified file 'debian/account-polld.conf' |
33 | --- debian/account-polld.conf 2014-08-06 14:29:13 +0000 |
34 | +++ debian/account-polld.conf 2015-10-12 13:54:21 +0000 |
35 | @@ -1,4 +1,4 @@ |
36 | -description "account-polld checks for notifications for twitter, facebook and gmail and passes them on to the Ubuntu Push Postal service" |
37 | +description "account-polld checks for notifications for twitter and gmail and passes them on to the Ubuntu Push Postal service" |
38 | |
39 | start on started ubuntu-push-client |
40 | stop on stopping ubuntu-push-client |
41 | |
42 | === modified file 'debian/changelog' |
43 | --- debian/changelog 2015-04-10 17:19:48 +0000 |
44 | +++ debian/changelog 2015-10-12 13:54:21 +0000 |
45 | @@ -1,3 +1,17 @@ |
46 | +account-polld (0.1+15.04.20151012-0ubuntu1) UNRELEASED; urgency=medium |
47 | + |
48 | + * Remove non-working facebook integration. (LP: #1493733) |
49 | + * debian/control, debian/account-polld.conf: |
50 | + Remove mentions of Facebook from the descriptions. |
51 | + |
52 | + -- Alberto Mardegan <alberto.mardegan@canonical.com> Mon, 12 Oct 2015 16:49:52 +0300 |
53 | + |
54 | +account-polld (0.1+15.04.20150410-0ubuntu2~gcc5.1) wily; urgency=medium |
55 | + |
56 | + * No-change test rebuild for g++5 ABI transition |
57 | + |
58 | + -- Steve Langasek <steve.langasek@ubuntu.com> Wed, 15 Jul 2015 07:18:29 +0000 |
59 | + |
60 | account-polld (0.1+15.04.20150410-0ubuntu1) vivid; urgency=medium |
61 | |
62 | [ John R. Lenton ] |
63 | |
64 | === modified file 'debian/control' |
65 | --- debian/control 2014-08-24 00:04:44 +0000 |
66 | +++ debian/control 2015-10-12 13:54:21 +0000 |
67 | @@ -29,7 +29,7 @@ |
68 | Built-Using: ${misc:Built-Using} |
69 | Recommends: accountsservice, |
70 | Description: Poll daemon for notifications though the Ubuntu Push Client |
71 | - This component polls facebook, twitter and gmail for updates and |
72 | + This component polls twitter and gmail for updates and |
73 | communicates with the postal service provided by the ubuntu push client |
74 | to expose notifications for the click webapps for the aforementioned |
75 | services. |
76 | |
77 | === removed directory 'plugins/facebook' |
78 | === removed file 'plugins/facebook/facebook.go' |
79 | --- plugins/facebook/facebook.go 2015-03-20 14:34:48 +0000 |
80 | +++ plugins/facebook/facebook.go 1970-01-01 00:00:00 +0000 |
81 | @@ -1,453 +0,0 @@ |
82 | -/* |
83 | - Copyright 2014 Canonical Ltd. |
84 | - |
85 | - This program is free software: you can redistribute it and/or modify it |
86 | - under the terms of the GNU General Public License version 3, as published |
87 | - by the Free Software Foundation. |
88 | - |
89 | - This program is distributed in the hope that it will be useful, but |
90 | - WITHOUT ANY WARRANTY; without even the implied warranties of |
91 | - MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
92 | - PURPOSE. See the GNU General Public License for more details. |
93 | - |
94 | - You should have received a copy of the GNU General Public License along |
95 | - with this program. If not, see <http://www.gnu.org/licenses/>. |
96 | -*/ |
97 | - |
98 | -package facebook |
99 | - |
100 | -import ( |
101 | - "encoding/json" |
102 | - "fmt" |
103 | - "net/http" |
104 | - "net/url" |
105 | - "os" |
106 | - "strings" |
107 | - "time" |
108 | - |
109 | - "log" |
110 | - |
111 | - "launchpad.net/account-polld/accounts" |
112 | - "launchpad.net/account-polld/gettext" |
113 | - "launchpad.net/account-polld/plugins" |
114 | -) |
115 | - |
116 | -const ( |
117 | - facebookTime = "2006-01-02T15:04:05-0700" |
118 | - maxIndividualNotifications = 2 |
119 | - consolidatedNotificationsIndexStart = maxIndividualNotifications |
120 | - maxIndividualThreads = 2 |
121 | - consolidatedThreadsIndexStart = maxIndividualThreads |
122 | - timeOffset = -5 |
123 | - pluginName = "facebook" |
124 | -) |
125 | - |
126 | -var baseUrl, _ = url.Parse("https://graph.facebook.com/v2.0/") |
127 | - |
128 | -type timeStamp string |
129 | - |
130 | -func (state fbState) persist(accountId uint) (err error) { |
131 | - err = plugins.Persist(pluginName, accountId, state) |
132 | - if err != nil { |
133 | - log.Print("facebook plugin", accountId, ": failed to save state: ", err) |
134 | - return err |
135 | - } |
136 | - return nil |
137 | -} |
138 | - |
139 | -func stateFromStorage(accountId uint) (state fbState, err error) { |
140 | - err = plugins.FromPersist(pluginName, accountId, &state) |
141 | - if err != nil { |
142 | - return state, err |
143 | - } |
144 | - if _, err := time.Parse(facebookTime, string(state.LastUpdate)); err != nil { |
145 | - return state, err |
146 | - } |
147 | - if _, err := time.Parse(facebookTime, string(state.LastInboxUpdate)); err != nil { |
148 | - return state, err |
149 | - } |
150 | - return state, nil |
151 | -} |
152 | - |
153 | -type fbState struct { |
154 | - LastUpdate timeStamp `json:"last_notification_update"` |
155 | - LastInboxUpdate timeStamp `json:"last_inbox_update"` |
156 | -} |
157 | - |
158 | -type fbPlugin struct { |
159 | - state fbState |
160 | - accountId uint |
161 | -} |
162 | - |
163 | -var doRequest = request |
164 | - |
165 | -func request(authData *accounts.AuthData, path string) (*http.Response, error) { |
166 | - // Resolve path relative to Graph API base URL, and add access token |
167 | - u, err := baseUrl.Parse(path) |
168 | - if err != nil { |
169 | - return nil, err |
170 | - } |
171 | - query := u.Query() |
172 | - query.Add("access_token", authData.AccessToken) |
173 | - u.RawQuery = query.Encode() |
174 | - |
175 | - return http.Get(u.String()) |
176 | -} |
177 | - |
178 | -func New(accountId uint) plugins.Plugin { |
179 | - state, err := stateFromStorage(accountId) |
180 | - if err != nil { |
181 | - log.Print("facebook plugin ", accountId, ": cannot load previous state from storage: ", err) |
182 | - } else { |
183 | - log.Print("facebook plugin ", accountId, ": last state loaded from storage") |
184 | - } |
185 | - return &fbPlugin{state: state, accountId: accountId} |
186 | -} |
187 | - |
188 | -func (p *fbPlugin) ApplicationId() plugins.ApplicationId { |
189 | - return "com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook" |
190 | -} |
191 | - |
192 | -func (p *fbPlugin) decodeResponse(resp *http.Response, result interface{}) error { |
193 | - defer resp.Body.Close() |
194 | - decoder := json.NewDecoder(resp.Body) |
195 | - if resp.StatusCode != http.StatusOK { |
196 | - var result errorDoc |
197 | - if err := decoder.Decode(&result); err != nil { |
198 | - return err |
199 | - } |
200 | - if result.Error.Code == 190 { |
201 | - return plugins.ErrTokenExpired |
202 | - } |
203 | - return &result.Error |
204 | - } |
205 | - // TODO: Follow the "paging.next" link if we get more than one |
206 | - // page full of notifications. |
207 | - return decoder.Decode(result) |
208 | -} |
209 | - |
210 | -func (p *fbPlugin) filterNotifications(doc Document, lastUpdate *timeStamp) []Notification { |
211 | - var validNotifications []Notification |
212 | - latestUpdate := *lastUpdate |
213 | - for i := 0; i < doc.size(); i++ { |
214 | - n := doc.notification(i) |
215 | - if !n.isValid(*lastUpdate) { |
216 | - log.Println("facebook plugin: skipping:", n) |
217 | - } else { |
218 | - log.Println("facebook plugin: valid:", n) |
219 | - validNotifications = append(validNotifications, n) // get the actual reference, not the copy |
220 | - if n.updatedTime() > latestUpdate { |
221 | - latestUpdate = n.updatedTime() |
222 | - } |
223 | - } |
224 | - } |
225 | - *lastUpdate = latestUpdate |
226 | - p.state.persist(p.accountId) |
227 | - return validNotifications |
228 | -} |
229 | - |
230 | -func (p *fbPlugin) buildPushMessages(notifications []Notification, doc Document, max int, consolidatedIndexStart int) *plugins.PushMessageBatch { |
231 | - pushMsg := make([]*plugins.PushMessage, len(notifications)) |
232 | - for i, n := range notifications { |
233 | - pushMsg[i] = n.buildPushMessage() |
234 | - } |
235 | - return &plugins.PushMessageBatch{ |
236 | - Messages: pushMsg, |
237 | - Limit: max, |
238 | - OverflowHandler: doc.handleOverflow, |
239 | - Tag: doc.getTag(), |
240 | - } |
241 | -} |
242 | - |
243 | -func (p *fbPlugin) parseResponse(resp *http.Response) (*plugins.PushMessageBatch, error) { |
244 | - var result notificationDoc |
245 | - if err := p.decodeResponse(resp, &result); err != nil { |
246 | - return nil, err |
247 | - } |
248 | - // TODO filter out of date messages before operating? |
249 | - validNotifications := p.filterNotifications(&result, &p.state.LastUpdate) |
250 | - pushMsgs := p.buildPushMessages(validNotifications, &result, maxIndividualNotifications, consolidatedNotificationsIndexStart) |
251 | - return pushMsgs, nil |
252 | -} |
253 | - |
254 | -func (p *fbPlugin) parseInboxResponse(resp *http.Response) (*plugins.PushMessageBatch, error) { |
255 | - var result inboxDoc |
256 | - if err := p.decodeResponse(resp, &result); err != nil { |
257 | - return nil, err |
258 | - } |
259 | - validThreads := p.filterNotifications(&result, &p.state.LastInboxUpdate) |
260 | - pushMsgs := p.buildPushMessages(validThreads, &result, maxIndividualThreads, consolidatedThreadsIndexStart) |
261 | - return pushMsgs, nil |
262 | -} |
263 | - |
264 | -func (p *fbPlugin) getNotifications(authData *accounts.AuthData) (*plugins.PushMessageBatch, error) { |
265 | - resp, err := doRequest(authData, "me/notifications") |
266 | - if err != nil { |
267 | - log.Println("facebook plugin: notifications poll failed: ", err) |
268 | - return nil, err |
269 | - } |
270 | - notifications, err := p.parseResponse(resp) |
271 | - if err != nil { |
272 | - log.Println("facebook plugin: failed to parse notification response: ", err) |
273 | - return nil, err |
274 | - } |
275 | - return notifications, nil |
276 | -} |
277 | - |
278 | -func (p *fbPlugin) getInbox(authData *accounts.AuthData) (*plugins.PushMessageBatch, error) { |
279 | - resp, err := doRequest(authData, "me/inbox?fields=unread,unseen,comments.limit(1)") |
280 | - if err != nil { |
281 | - log.Println("facebook plugin: inbox poll failed: ", err) |
282 | - return nil, err |
283 | - } |
284 | - inbox, err := p.parseInboxResponse(resp) |
285 | - if err != nil { |
286 | - log.Println("facebook plugin: failed to parse inbox response: ", err) |
287 | - return nil, err |
288 | - } |
289 | - return inbox, nil |
290 | -} |
291 | - |
292 | -func (p *fbPlugin) Poll(authData *accounts.AuthData) ([]*plugins.PushMessageBatch, error) { |
293 | - // This envvar check is to ease testing. |
294 | - if token := os.Getenv("ACCOUNT_POLLD_TOKEN_FACEBOOK"); token != "" { |
295 | - authData.AccessToken = token |
296 | - } |
297 | - notifications, notifErr := p.getNotifications(authData) |
298 | - inbox, inboxErr := p.getInbox(authData) |
299 | - // only return error if both requests failed |
300 | - if notifErr != nil && inboxErr != nil { |
301 | - return nil, fmt.Errorf("poll failed with '%s' and '%s'", notifErr, inboxErr) |
302 | - } |
303 | - if notifErr != nil { |
304 | - return []*plugins.PushMessageBatch{inbox}, nil |
305 | - } |
306 | - if inboxErr != nil { |
307 | - return []*plugins.PushMessageBatch{notifications}, nil |
308 | - } |
309 | - return []*plugins.PushMessageBatch{notifications, inbox}, nil |
310 | -} |
311 | - |
312 | -func toEpoch(stamp timeStamp) int64 { |
313 | - if t, err := time.Parse(facebookTime, string(stamp)); err == nil { |
314 | - return t.Unix() |
315 | - } |
316 | - return time.Now().Unix() |
317 | -} |
318 | - |
319 | -// The notifications response format is described here: |
320 | -// https://developers.facebook.com/docs/graph-api/reference/v2.0/user/notifications/ |
321 | -type notificationDoc struct { |
322 | - Data []notification `json:"data"` |
323 | - Paging struct { |
324 | - Previous string `json:"previous"` |
325 | - Next string `json:"next"` |
326 | - } `json:"paging"` |
327 | -} |
328 | - |
329 | -type notification struct { |
330 | - Id string `json:"id"` |
331 | - From object `json:"from"` |
332 | - To object `json:"to"` |
333 | - CreatedTime timeStamp `json:"created_time"` |
334 | - UpdatedTime timeStamp `json:"updated_time"` |
335 | - Title string `json:"title"` |
336 | - Link string `json:"link"` |
337 | - Application object `json:"application"` |
338 | - Unread int `json:"unread"` |
339 | - Object object `json:"object"` |
340 | -} |
341 | - |
342 | -func picture(msgId string, id string) string { |
343 | - u, err := baseUrl.Parse(fmt.Sprintf("%s/picture", id)) |
344 | - if err != nil { |
345 | - log.Println("facebook plugin: cannot get picture for", msgId) |
346 | - return "" |
347 | - } |
348 | - query := u.Query() |
349 | - query.Add("redirect", "true") |
350 | - u.RawQuery = query.Encode() |
351 | - return u.String() |
352 | -} |
353 | - |
354 | -type object struct { |
355 | - Id string `json:"id"` |
356 | - Name string `json:"name"` |
357 | -} |
358 | - |
359 | -// The error response format is described here: |
360 | -// https://developers.facebook.com/docs/graph-api/using-graph-api/v2.0#errors |
361 | -type errorDoc struct { |
362 | - Error GraphError `json:"error"` |
363 | -} |
364 | - |
365 | -type GraphError struct { |
366 | - Message string `json:"message"` |
367 | - Type string `json:"type"` |
368 | - Code int `json:"code"` |
369 | - Subcode int `json:"error_subcode"` |
370 | -} |
371 | - |
372 | -func (err *GraphError) Error() string { |
373 | - return err.Message |
374 | -} |
375 | - |
376 | -// The inbox response format is described here: |
377 | -// https://developers.facebook.com/docs/graph-api/reference/v2.0/user/inbox |
378 | -type inboxDoc struct { |
379 | - Data []thread `json:"data"` |
380 | - Paging struct { |
381 | - Previous string `json:"previous"` |
382 | - Next string `json:"next"` |
383 | - } `json:"paging"` |
384 | - Summary summary `json:"summary"` |
385 | -} |
386 | - |
387 | -type summary struct { |
388 | - UnseenCount int `json:"unseen_count"` |
389 | - UnreadCount int `json:"unread_count"` |
390 | - UpdatedTime timeStamp `json:"updated_time"` |
391 | -} |
392 | - |
393 | -type thread struct { |
394 | - Id string `json:"id"` |
395 | - Comments comments `json:"comments"` |
396 | - To []object `json:"to"` |
397 | - Unread int `json:"unread"` |
398 | - Unseen int `json:"unseen"` |
399 | - UpdatedTime timeStamp `json:"updated_time"` |
400 | - Paging struct { |
401 | - Previous string `json:"previous"` |
402 | - Next string `json:"next"` |
403 | - } `json:"paging"` |
404 | -} |
405 | - |
406 | -type comments struct { |
407 | - Data []message `json:"data"` |
408 | - Paging struct { |
409 | - Previous string `json:"previous"` |
410 | - Next string `json:"next"` |
411 | - } `json:"paging"` |
412 | -} |
413 | - |
414 | -type message struct { |
415 | - CreatedTime timeStamp `json:"created_time"` |
416 | - From object `json:"from"` |
417 | - Id string `json:"id"` |
418 | - Message string `json:message` |
419 | -} |
420 | - |
421 | -func (doc *inboxDoc) getTag() string { |
422 | - return "inbox" |
423 | -} |
424 | - |
425 | -func (doc *inboxDoc) handleOverflow(pushMsg []*plugins.PushMessage) *plugins.PushMessage { |
426 | - usernames := []string{} |
427 | - for _, m := range pushMsg { |
428 | - usernames = append(usernames, m.Notification.Card.Summary) |
429 | - if len(usernames) > 10 { |
430 | - usernames[10] = "…" |
431 | - break |
432 | - } |
433 | - } |
434 | - // TRANSLATORS: This represents a message summary about more facebook messages |
435 | - summary := gettext.Gettext("Multiple more messages") |
436 | - // TRANSLATORS: This represents a message body with the comma separated facebook usernames |
437 | - body := fmt.Sprintf(gettext.Gettext("From %s"), strings.Join(usernames, ", ")) |
438 | - action := "https://m.facebook.com/messages" |
439 | - epoch := time.Now().Unix() |
440 | - return plugins.NewStandardPushMessage(summary, body, action, "", epoch) |
441 | -} |
442 | - |
443 | -func (doc *inboxDoc) size() int { |
444 | - return len(doc.Data) |
445 | -} |
446 | - |
447 | -func (doc *inboxDoc) notification(idx int) Notification { |
448 | - return &doc.Data[idx] |
449 | -} |
450 | - |
451 | -func (t *thread) buildPushMessage() *plugins.PushMessage { |
452 | - link := "https://www.facebook.com/messages?action=recent-messages" |
453 | - epoch := toEpoch(t.UpdatedTime) |
454 | - // get the single message we fetch |
455 | - message := t.Comments.Data[0] |
456 | - return plugins.NewStandardPushMessage(message.From.Name, message.Message, link, picture(t.Id, message.From.Id), epoch) |
457 | -} |
458 | - |
459 | -func (t *thread) isValid(tStamp timeStamp) bool { |
460 | - limit := timeStamp(time.Now().Add(timeOffset * time.Minute).Format(facebookTime)) |
461 | - return tStamp < t.UpdatedTime && t.UpdatedTime < limit && t.Unread != 0 && t.Unseen != 0 |
462 | -} |
463 | - |
464 | -func (t *thread) updatedTime() timeStamp { |
465 | - return t.UpdatedTime |
466 | -} |
467 | - |
468 | -func (t *thread) String() string { |
469 | - return fmt.Sprintf("id: %s, dated: %s, unread: %d, unseen: %d", t.Id, t.UpdatedTime, t.Unread, t.Unseen) |
470 | -} |
471 | - |
472 | -func (doc *notificationDoc) getTag() string { |
473 | - return "notification" |
474 | -} |
475 | - |
476 | -func (doc *notificationDoc) handleOverflow(pushMsg []*plugins.PushMessage) *plugins.PushMessage { |
477 | - usernames := []string{} |
478 | - for _, m := range pushMsg { |
479 | - usernames = append(usernames, m.Notification.Card.Summary) |
480 | - if len(usernames) > 10 { |
481 | - usernames[10] = "…" |
482 | - break |
483 | - } |
484 | - } |
485 | - // TRANSLATORS: This represents a notification summary about more facebook notifications |
486 | - summary := gettext.Gettext("Multiple more notifications") |
487 | - // TRANSLATORS: This represents a notification body with the comma separated facebook usernames |
488 | - body := fmt.Sprintf(gettext.Gettext("From %s"), strings.Join(usernames, ", ")) |
489 | - action := "https://m.facebook.com" |
490 | - epoch := time.Now().Unix() |
491 | - return plugins.NewStandardPushMessage(summary, body, action, "", epoch) |
492 | -} |
493 | - |
494 | -func (doc *notificationDoc) size() int { |
495 | - return len(doc.Data) |
496 | -} |
497 | - |
498 | -func (doc *notificationDoc) notification(idx int) Notification { |
499 | - return &doc.Data[idx] |
500 | -} |
501 | - |
502 | -func (n *notification) buildPushMessage() *plugins.PushMessage { |
503 | - epoch := toEpoch(n.UpdatedTime) |
504 | - return plugins.NewStandardPushMessage(n.From.Name, n.Title, n.Link, picture(n.Id, n.From.Id), epoch) |
505 | -} |
506 | - |
507 | -func (n *notification) isValid(tStamp timeStamp) bool { |
508 | - return n.UpdatedTime > tStamp && n.Unread >= 1 |
509 | -} |
510 | - |
511 | -func (n *notification) updatedTime() timeStamp { |
512 | - return n.UpdatedTime |
513 | -} |
514 | - |
515 | -func (n *notification) String() string { |
516 | - return fmt.Sprintf("id: %s, dated: %s, unread: %d", n.Id, n.UpdatedTime, n.Unread) |
517 | -} |
518 | - |
519 | -type Document interface { |
520 | - getTag() string |
521 | - handleOverflow([]*plugins.PushMessage) *plugins.PushMessage |
522 | - size() int |
523 | - notification(int) Notification |
524 | -} |
525 | - |
526 | -type Notification interface { |
527 | - buildPushMessage() *plugins.PushMessage |
528 | - isValid(timeStamp) bool |
529 | - updatedTime() timeStamp |
530 | - String() string |
531 | -} |
532 | - |
533 | -var _ Notification = (*thread)(nil) |
534 | -var _ Notification = (*notification)(nil) |
535 | |
536 | === removed file 'plugins/facebook/facebook_test.go' |
537 | --- plugins/facebook/facebook_test.go 2015-03-20 14:34:48 +0000 |
538 | +++ plugins/facebook/facebook_test.go 1970-01-01 00:00:00 +0000 |
539 | @@ -1,835 +0,0 @@ |
540 | -/* |
541 | - Copyright 2014 Canonical Ltd. |
542 | - |
543 | - This program is free software: you can redistribute it and/or modify it |
544 | - under the terms of the GNU General Public License version 3, as published |
545 | - by the Free Software Foundation. |
546 | - |
547 | - This program is distributed in the hope that it will be useful, but |
548 | - WITHOUT ANY WARRANTY; without even the implied warranties of |
549 | - MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
550 | - PURPOSE. See the GNU General Public License for more details. |
551 | - |
552 | - You should have received a copy of the GNU General Public License along |
553 | - with this program. If not, see <http://www.gnu.org/licenses/>. |
554 | -*/ |
555 | -package facebook |
556 | - |
557 | -import ( |
558 | - "bytes" |
559 | - "encoding/json" |
560 | - "fmt" |
561 | - "io" |
562 | - "io/ioutil" |
563 | - "net/http" |
564 | - "net/url" |
565 | - "os" |
566 | - "path" |
567 | - "path/filepath" |
568 | - "time" |
569 | - |
570 | - "net/http/httptest" |
571 | - "testing" |
572 | - |
573 | - . "launchpad.net/gocheck" |
574 | - |
575 | - "launchpad.net/account-polld/accounts" |
576 | - "launchpad.net/account-polld/plugins" |
577 | - "launchpad.net/go-xdg/v0" |
578 | -) |
579 | - |
580 | -type S struct { |
581 | - tempDir string |
582 | - ts *httptest.Server |
583 | -} |
584 | - |
585 | -var _ = Suite(&S{}) |
586 | - |
587 | -func Test(t *testing.T) { TestingT(t) } |
588 | - |
589 | -// closeWraper adds a dummy Close() method to a reader |
590 | -type closeWrapper struct { |
591 | - io.Reader |
592 | -} |
593 | - |
594 | -func (r closeWrapper) Close() error { |
595 | - return nil |
596 | -} |
597 | - |
598 | -const ( |
599 | - errorBody = ` |
600 | -{ |
601 | - "error": { |
602 | - "message": "Unknown path components: /xyz", |
603 | - "type": "OAuthException", |
604 | - "code": 2500 |
605 | - } |
606 | -}` |
607 | - tokenExpiredErrorBody = ` |
608 | -{ |
609 | - "error": { |
610 | - "message": "Error validating access token: Session has expired", |
611 | - "type": "OAuthException", |
612 | - "code": 190 , |
613 | - "error_subcode": 463 |
614 | - } |
615 | -}` |
616 | - notificationsBody = ` |
617 | -{ |
618 | - "data": [ |
619 | - { |
620 | - "id": "notif_id", |
621 | - "from": { |
622 | - "id": "sender_id", |
623 | - "name": "Sender" |
624 | - }, |
625 | - "to": { |
626 | - "id": "recipient_id", |
627 | - "name": "Recipient" |
628 | - }, |
629 | - "created_time": "2014-07-12T09:51:57+0000", |
630 | - "updated_time": "2014-07-12T09:51:57+0000", |
631 | - "title": "Sender posted on your timeline: \"The message...\"", |
632 | - "link": "http://www.facebook.com/recipient/posts/id", |
633 | - "application": { |
634 | - "name": "Wall", |
635 | - "namespace": "wall", |
636 | - "id": "2719290516" |
637 | - }, |
638 | - "unread": 1 |
639 | - }, |
640 | - { |
641 | - "id": "notif_1105650586_80600069", |
642 | - "from": { |
643 | - "id": "sender2_id", |
644 | - "name": "Sender2" |
645 | - }, |
646 | - "to": { |
647 | - "id": "recipient_id", |
648 | - "name": "Recipient" |
649 | - }, |
650 | - "created_time": "2014-07-08T06:17:52+0000", |
651 | - "updated_time": "2014-07-08T06:17:52+0000", |
652 | - "title": "Sender2's birthday was on July 7.", |
653 | - "link": "http://www.facebook.com/profile.php?id=xxx&ref=brem", |
654 | - "application": { |
655 | - "name": "Gifts", |
656 | - "namespace": "superkarma", |
657 | - "id": "329122197162272" |
658 | - }, |
659 | - "unread": 1, |
660 | - "object": { |
661 | - "id": "sender2_id", |
662 | - "name": "Sender2" |
663 | - } |
664 | - } |
665 | - ], |
666 | - "paging": { |
667 | - "previous": "https://graph.facebook.com/v2.0/recipient/notifications?limit=5000&since=1405158717&__paging_token=enc_AewDzwIQmWOwPNO-36GaZsaJAog8l93HQ7uLEO-gp1Tb6KCiolXfzMCcGY2KjrJJsDJXdDmNJObICr5dewfMZgGs", |
668 | - "next": "https://graph.facebook.com/v2.0/recipient/notifications?limit=5000&until=1404705077&__paging_token=enc_Aewlhut5DQyhqtLNr7pLCMlYU012t4XY7FOt7cooz4wsWIWi-Jqz0a0IDnciJoeLu2vNNQkbtOpCmEmsVsN4hkM4" |
669 | - }, |
670 | - "summary": [ |
671 | - ] |
672 | -} |
673 | -` |
674 | - largeNotificationsBody = ` |
675 | -{ |
676 | - "data": [ |
677 | - { |
678 | - "id": "notif_id", |
679 | - "from": { |
680 | - "id": "sender_id", |
681 | - "name": "Sender" |
682 | - }, |
683 | - "to": { |
684 | - "id": "recipient_id", |
685 | - "name": "Recipient" |
686 | - }, |
687 | - "created_time": "2014-07-12T09:51:57+0000", |
688 | - "updated_time": "2014-07-12T09:51:57+0000", |
689 | - "title": "Sender posted on your timeline: \"The message...\"", |
690 | - "link": "http://www.facebook.com/recipient/posts/id", |
691 | - "application": { |
692 | - "name": "Wall", |
693 | - "namespace": "wall", |
694 | - "id": "2719290516" |
695 | - }, |
696 | - "unread": 1 |
697 | - }, |
698 | - { |
699 | - "id": "notif_1105650586_80600069", |
700 | - "from": { |
701 | - "id": "sender2_id", |
702 | - "name": "Sender2" |
703 | - }, |
704 | - "to": { |
705 | - "id": "recipient_id", |
706 | - "name": "Recipient" |
707 | - }, |
708 | - "created_time": "2014-07-08T06:17:52+0000", |
709 | - "updated_time": "2014-07-08T06:17:52+0000", |
710 | - "title": "Sender2's birthday was on July 7.", |
711 | - "link": "http://www.facebook.com/profile.php?id=xxx&ref=brem", |
712 | - "application": { |
713 | - "name": "Gifts", |
714 | - "namespace": "superkarma", |
715 | - "id": "329122197162272" |
716 | - }, |
717 | - "unread": 1, |
718 | - "object": { |
719 | - "id": "sender2_id", |
720 | - "name": "Sender2" |
721 | - } |
722 | - }, |
723 | - { |
724 | - "id": "notif_id_3", |
725 | - "from": { |
726 | - "id": "sender3_id", |
727 | - "name": "Sender3" |
728 | - }, |
729 | - "to": { |
730 | - "id": "recipient_id", |
731 | - "name": "Recipient" |
732 | - }, |
733 | - "created_time": "2014-07-12T09:51:57+0000", |
734 | - "updated_time": "2014-07-12T09:51:57+0000", |
735 | - "title": "Sender posted on your timeline: \"The message...\"", |
736 | - "link": "http://www.facebook.com/recipient/posts/id", |
737 | - "application": { |
738 | - "name": "Wall", |
739 | - "namespace": "wall", |
740 | - "id": "2719290516" |
741 | - }, |
742 | - "unread": 1 |
743 | - }, |
744 | - { |
745 | - "id": "notif_id_4", |
746 | - "from": { |
747 | - "id": "sender4_id", |
748 | - "name": "Sender2" |
749 | - }, |
750 | - "to": { |
751 | - "id": "recipient_id", |
752 | - "name": "Recipient" |
753 | - }, |
754 | - "created_time": "2014-07-08T06:17:52+0000", |
755 | - "updated_time": "2014-07-08T06:17:52+0000", |
756 | - "title": "Sender2's birthday was on July 7.", |
757 | - "link": "http://www.facebook.com/profile.php?id=xxx&ref=brem", |
758 | - "application": { |
759 | - "name": "Gifts", |
760 | - "namespace": "superkarma", |
761 | - "id": "329122197162272" |
762 | - }, |
763 | - "unread": 1 |
764 | - } |
765 | - ], |
766 | - "paging": { |
767 | - "previous": "https://graph.facebook.com/v2.0/recipient/notifications?limit=5000&since=1405158717&__paging_token=enc_AewDzwIQmWOwPNO-36GaZsaJAog8l93HQ7uLEO-gp1Tb6KCiolXfzMCcGY2KjrJJsDJXdDmNJObICr5dewfMZgGs", |
768 | - "next": "https://graph.facebook.com/v2.0/recipient/notifications?limit=5000&until=1404705077&__paging_token=enc_Aewlhut5DQyhqtLNr7pLCMlYU012t4XY7FOt7cooz4wsWIWi-Jqz0a0IDnciJoeLu2vNNQkbtOpCmEmsVsN4hkM4" |
769 | - }, |
770 | - "summary": [ |
771 | - ] |
772 | -} |
773 | -` |
774 | - |
775 | - inboxBody = ` |
776 | -{ |
777 | - "data": [ |
778 | - { |
779 | - "unread": 1, |
780 | - "unseen": 1, |
781 | - "id": "445809168892281", |
782 | - "updated_time": "2014-08-25T18:39:32+0000", |
783 | - "comments": { |
784 | - "data": [ |
785 | - { |
786 | - "id": "445809168892281_1408991972", |
787 | - "from": { |
788 | - "id": "346217352202239", |
789 | - "name": "Pollod Magnifico" |
790 | - }, |
791 | - "message": "Hola mundo!", |
792 | - "created_time": "2014-08-25T18:39:32+0000" |
793 | - } |
794 | - ], |
795 | - "paging": { |
796 | - "previous": "https://graph.facebook.com/v2.0/445809168892281/comments?limit=1&since=1408991972&__paging_token=enc_Aew2kKJXEXzdm9k89DvLYz_y8nYxUbvElWcn6h_pKMRsoAPTPpkU7-AsGhkcYF6M1qbomOnFJf9ckL5J3hTltLFq", |
797 | - "next": "https://graph.facebook.com/v2.0/445809168892281/comments?limit=1&until=1408991972&__paging_token=enc_Aewlixpk4h4Vq79-W1ixrTM6ONbsMUDrcj0vLABs34tbhWarfpQLf818uoASWNDEpQO4XEXh5HbgHpcCqnuNVEOR" |
798 | - } |
799 | - } |
800 | - }, |
801 | - { |
802 | - "unread": 2, |
803 | - "unseen": 1, |
804 | - "id": "445809168892282", |
805 | - "updated_time": "2014-08-25T18:39:32+0000", |
806 | - "comments": { |
807 | - "data": [ |
808 | - { |
809 | - "id": "445809168892282_1408991973", |
810 | - "from": { |
811 | - "id": "346217352202239", |
812 | - "name": "Pollitod Magnifico" |
813 | - }, |
814 | - "message": "Hola!", |
815 | - "created_time": "2014-08-25T18:39:32+0000" |
816 | - } |
817 | - ], |
818 | - "paging": { |
819 | - "previous": "https://graph.facebook.com/v2.0/445809168892281/comments?limit=1&since=1408991972&__paging_token=enc_Aew2kKJXEXzdm9k89DvLYz_y8nYxUbvElWcn6h_pKMRsoAPTPpkU7-AsGhkcYF6M1qbomOnFJf9ckL5J3hTltLFq", |
820 | - "next": "https://graph.facebook.com/v2.0/445809168892281/comments?limit=1&until=1408991972&__paging_token=enc_Aewlixpk4h4Vq79-W1ixrTM6ONbsMUDrcj0vLABs34tbhWarfpQLf818uoASWNDEpQO4XEXh5HbgHpcCqnuNVEOR" |
821 | - } |
822 | - } |
823 | - }, |
824 | - { |
825 | - "unread": 2, |
826 | - "unseen": 1, |
827 | - "id": "445809168892283", |
828 | - "updated_time": "2014-08-25T18:39:32+0000", |
829 | - "comments": { |
830 | - "data": [ |
831 | - { |
832 | - "id": "445809168892282_1408991973", |
833 | - "from": { |
834 | - "id": "346217352202240", |
835 | - "name": "A Friend" |
836 | - }, |
837 | - "message": "mellon", |
838 | - "created_time": "2014-08-25T18:39:32+0000" |
839 | - } |
840 | - ], |
841 | - "paging": { |
842 | - "previous": "https://graph.facebook.com/v2.0/445809168892281/comments?limit=1&since=1408991972&__paging_token=enc_Aew2kKJXEXzdm9k89DvLYz_y8nYxUbvElWcn6h_pKMRsoAPTPpkU7-AsGhkcYF6M1qbomOnFJf9ckL5J3hTltLFq", |
843 | - "next": "https://graph.facebook.com/v2.0/445809168892281/comments?limit=1&until=1408991972&__paging_token=enc_Aewlixpk4h4Vq79-W1ixrTM6ONbsMUDrcj0vLABs34tbhWarfpQLf818uoASWNDEpQO4XEXh5HbgHpcCqnuNVEOR" |
844 | - } |
845 | - } |
846 | - } |
847 | - |
848 | - |
849 | - ], |
850 | - "paging": { |
851 | - "previous": "https://graph.facebook.com/v2.0/270128826512416/inbox?fields=unread,unseen,comments.limit(1)&limit=25&since=1408991972&__paging_token=enc_Aey99ACSOyZqN_7I-yWLnY8K3dqu4wVsx-Th3kMHMTMQ5VPbQRPgCQiJps0II1QAXDAVzHplqPS8yNgq8Zs_G2aK", |
852 | - "next": "https://graph.facebook.com/v2.0/270128826512416/inbox?fields=unread,unseen,comments.limit(1)&limit=25&until=1408991972&__paging_token=enc_AewjHkk10NNjRCXJCoaP5hyf22kw-htwxsDaVOiLY-IiXxB99sKNGlfFFmkcG-VeMGUETI2agZGR_1IWP5W4vyPL" |
853 | - }, |
854 | - "summary": { |
855 | - "unseen_count": 0, |
856 | - "unread_count": 1, |
857 | - "updated_time": "2014-08-25T19:05:49+0000" |
858 | - } |
859 | -} |
860 | -` |
861 | -) |
862 | - |
863 | -func (s *S) SetUpTest(c *C) { |
864 | - s.tempDir = c.MkDir() |
865 | - plugins.XdgDataFind = func(a string) (string, error) { |
866 | - return filepath.Join(s.tempDir, a), nil |
867 | - } |
868 | - plugins.XdgDataEnsure = func(a string) (string, error) { |
869 | - p := filepath.Join(s.tempDir, a) |
870 | - base := path.Dir(p) |
871 | - if _, err := os.Stat(base); err != nil { |
872 | - os.MkdirAll(base, 0700) |
873 | - } |
874 | - return p, nil |
875 | - } |
876 | - s.ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
877 | - fmt.Fprintln(w, "Hello, client") |
878 | - })) |
879 | - baseUrl, _ = url.Parse(s.ts.URL) |
880 | -} |
881 | - |
882 | -func (s *S) TearDownTest(c *C) { |
883 | - plugins.XdgDataFind = xdg.Data.Find |
884 | - plugins.XdgDataEnsure = xdg.Data.Find |
885 | - doRequest = request |
886 | - s.ts.Close() |
887 | -} |
888 | - |
889 | -func (s *S) TestParseNotifications(c *C) { |
890 | - resp := &http.Response{ |
891 | - StatusCode: http.StatusOK, |
892 | - Body: closeWrapper{bytes.NewReader([]byte(notificationsBody))}, |
893 | - } |
894 | - p := &fbPlugin{} |
895 | - batch, err := p.parseResponse(resp) |
896 | - c.Assert(err, IsNil) |
897 | - c.Assert(batch, NotNil) |
898 | - messages := batch.Messages |
899 | - c.Assert(len(messages), Equals, 2) |
900 | - c.Check(messages[0].Notification.Card.Summary, Equals, "Sender") |
901 | - c.Check(messages[0].Notification.Card.Body, Equals, "Sender posted on your timeline: \"The message...\"") |
902 | - c.Check(messages[1].Notification.Card.Summary, Equals, "Sender2") |
903 | - c.Check(messages[1].Notification.Card.Body, Equals, "Sender2's birthday was on July 7.") |
904 | - c.Check(p.state.LastUpdate, Equals, timeStamp("2014-07-12T09:51:57+0000")) |
905 | -} |
906 | - |
907 | -func (s *S) TestParseLotsOfNotifications(c *C) { |
908 | - resp := &http.Response{ |
909 | - StatusCode: http.StatusOK, |
910 | - Body: closeWrapper{bytes.NewReader([]byte(largeNotificationsBody))}, |
911 | - } |
912 | - p := &fbPlugin{} |
913 | - batch, err := p.parseResponse(resp) |
914 | - c.Assert(err, IsNil) |
915 | - c.Assert(batch, NotNil) |
916 | - messages := batch.Messages |
917 | - c.Assert(len(messages), Equals, 4) |
918 | - c.Check(messages[0].Notification.Card.Summary, Equals, "Sender") |
919 | - c.Check(messages[0].Notification.Card.Body, Equals, "Sender posted on your timeline: \"The message...\"") |
920 | - c.Check(messages[1].Notification.Card.Summary, Equals, "Sender2") |
921 | - c.Check(messages[1].Notification.Card.Body, Equals, "Sender2's birthday was on July 7.") |
922 | - ofMsg := batch.OverflowHandler(messages[2:]) |
923 | - c.Check(ofMsg.Notification.Card.Summary, Equals, "Multiple more notifications") |
924 | - c.Check(ofMsg.Notification.Card.Body, Equals, "From Sender3, Sender2") |
925 | - c.Check(p.state.LastUpdate, Equals, timeStamp("2014-07-12T09:51:57+0000")) |
926 | -} |
927 | - |
928 | -func (s *S) TestIgnoreOldNotifications(c *C) { |
929 | - resp := &http.Response{ |
930 | - StatusCode: http.StatusOK, |
931 | - Body: closeWrapper{bytes.NewReader([]byte(notificationsBody))}, |
932 | - } |
933 | - p := &fbPlugin{state: fbState{LastUpdate: "2014-07-08T06:17:52+0000"}} |
934 | - batch, err := p.parseResponse(resp) |
935 | - c.Assert(err, IsNil) |
936 | - c.Assert(batch, NotNil) |
937 | - messages := batch.Messages |
938 | - c.Assert(len(messages), Equals, 1) |
939 | - c.Check(messages[0].Notification.Card.Summary, Equals, "Sender") |
940 | - c.Check(messages[0].Notification.Card.Body, Equals, "Sender posted on your timeline: \"The message...\"") |
941 | - c.Check(p.state.LastUpdate, Equals, timeStamp("2014-07-12T09:51:57+0000")) |
942 | -} |
943 | - |
944 | -func (s *S) TestParseResponseErrorResponse(c *C) { |
945 | - resp := &http.Response{ |
946 | - StatusCode: http.StatusBadRequest, |
947 | - Body: closeWrapper{bytes.NewReader([]byte(errorBody))}, |
948 | - } |
949 | - p := &fbPlugin{} |
950 | - notifications, err := p.parseResponse(resp) |
951 | - c.Check(notifications, IsNil) |
952 | - c.Assert(err, Not(IsNil)) |
953 | - graphErr := err.(*GraphError) |
954 | - c.Check(graphErr.Message, Equals, "Unknown path components: /xyz") |
955 | - c.Check(graphErr.Code, Equals, 2500) |
956 | -} |
957 | - |
958 | -func (s *S) TestDecodeResponseErrorResponse(c *C) { |
959 | - resp := &http.Response{ |
960 | - StatusCode: http.StatusBadRequest, |
961 | - Body: closeWrapper{bytes.NewReader([]byte(errorBody))}, |
962 | - } |
963 | - p := &fbPlugin{} |
964 | - var result notificationDoc |
965 | - err := p.decodeResponse(resp, &result) |
966 | - c.Check(result, DeepEquals, notificationDoc{}) |
967 | - c.Assert(err, Not(IsNil)) |
968 | - graphErr := err.(*GraphError) |
969 | - c.Check(graphErr.Message, Equals, "Unknown path components: /xyz") |
970 | - c.Check(graphErr.Code, Equals, 2500) |
971 | -} |
972 | - |
973 | -func (s *S) TestDecodeResponseErrorResponseFails(c *C) { |
974 | - resp := &http.Response{ |
975 | - StatusCode: http.StatusBadRequest, |
976 | - Body: closeWrapper{bytes.NewReader([]byte("hola" + errorBody))}, |
977 | - } |
978 | - p := &fbPlugin{} |
979 | - var result notificationDoc |
980 | - err := p.decodeResponse(resp, &result) |
981 | - c.Check(result, DeepEquals, notificationDoc{}) |
982 | - c.Assert(err, NotNil) |
983 | - jsonErr := err.(*json.SyntaxError) |
984 | - c.Check(jsonErr.Offset, Equals, int64(1)) |
985 | -} |
986 | - |
987 | -func (s *S) TestTokenExpiredErrorResponse(c *C) { |
988 | - resp := &http.Response{ |
989 | - StatusCode: http.StatusBadRequest, |
990 | - Body: closeWrapper{bytes.NewReader([]byte(tokenExpiredErrorBody))}, |
991 | - } |
992 | - p := &fbPlugin{} |
993 | - notifications, err := p.parseResponse(resp) |
994 | - c.Check(notifications, IsNil) |
995 | - c.Assert(err, Equals, plugins.ErrTokenExpired) |
996 | -} |
997 | - |
998 | -func (s *S) TestParseInbox(c *C) { |
999 | - resp := &http.Response{ |
1000 | - StatusCode: http.StatusOK, |
1001 | - Body: closeWrapper{bytes.NewReader([]byte(inboxBody))}, |
1002 | - } |
1003 | - p := &fbPlugin{} |
1004 | - batch, err := p.parseInboxResponse(resp) |
1005 | - c.Assert(err, IsNil) |
1006 | - c.Assert(batch, NotNil) |
1007 | - messages := batch.Messages |
1008 | - c.Assert(len(messages), Equals, 3) |
1009 | - c.Check(messages[0].Notification.Card.Summary, Equals, "Pollod Magnifico") |
1010 | - c.Check(messages[0].Notification.Card.Body, Equals, "Hola mundo!") |
1011 | - c.Check(messages[1].Notification.Card.Summary, Equals, "Pollitod Magnifico") |
1012 | - c.Check(messages[1].Notification.Card.Body, Equals, "Hola!") |
1013 | - |
1014 | - ofMsg := batch.OverflowHandler(messages[batch.Limit:]) |
1015 | - |
1016 | - c.Check(ofMsg.Notification.Card.Summary, Equals, "Multiple more messages") |
1017 | - c.Check(ofMsg.Notification.Card.Body, Equals, "From A Friend") |
1018 | - c.Check(p.state.LastInboxUpdate, Equals, timeStamp("2014-08-25T18:39:32+0000")) |
1019 | -} |
1020 | - |
1021 | -func (s *S) TestDecodeResponse(c *C) { |
1022 | - resp := &http.Response{ |
1023 | - StatusCode: http.StatusOK, |
1024 | - Body: closeWrapper{bytes.NewReader([]byte(inboxBody))}, |
1025 | - } |
1026 | - p := &fbPlugin{} |
1027 | - var doc inboxDoc |
1028 | - err := p.decodeResponse(resp, &doc) |
1029 | - c.Check(err, IsNil) |
1030 | - c.Check(len(doc.Data), Equals, 3) |
1031 | -} |
1032 | - |
1033 | -func (s *S) TestDecodeResponseFails(c *C) { |
1034 | - resp := &http.Response{ |
1035 | - StatusCode: http.StatusOK, |
1036 | - Body: closeWrapper{bytes.NewReader([]byte("hola" + inboxBody))}, |
1037 | - } |
1038 | - p := &fbPlugin{} |
1039 | - var doc inboxDoc |
1040 | - err := p.decodeResponse(resp, &doc) |
1041 | - c.Assert(err, NotNil) |
1042 | - jsonErr := err.(*json.SyntaxError) |
1043 | - c.Check(jsonErr.Offset, Equals, int64(1)) |
1044 | -} |
1045 | - |
1046 | -func (s *S) TestFilterNotifications(c *C) { |
1047 | - resp := &http.Response{ |
1048 | - StatusCode: http.StatusOK, |
1049 | - Body: closeWrapper{bytes.NewReader([]byte(inboxBody))}, |
1050 | - } |
1051 | - p := &fbPlugin{} |
1052 | - var doc inboxDoc |
1053 | - p.decodeResponse(resp, &doc) |
1054 | - var state fbState |
1055 | - notifications := p.filterNotifications(&doc, &state.LastInboxUpdate) |
1056 | - c.Check(notifications, HasLen, 3) |
1057 | - // check if the lastInboxUpdate is updated |
1058 | - c.Check(state.LastInboxUpdate, Equals, timeStamp("2014-08-25T18:39:32+0000")) |
1059 | -} |
1060 | - |
1061 | -func (s *S) TestBuildPushMessages(c *C) { |
1062 | - resp := &http.Response{ |
1063 | - StatusCode: http.StatusOK, |
1064 | - Body: closeWrapper{bytes.NewReader([]byte(inboxBody))}, |
1065 | - } |
1066 | - var state fbState |
1067 | - p := &fbPlugin{state: state, accountId: 32} |
1068 | - var doc inboxDoc |
1069 | - p.decodeResponse(resp, &doc) |
1070 | - notifications := p.filterNotifications(&doc, &p.state.LastInboxUpdate) |
1071 | - batch := p.buildPushMessages(notifications, &doc, doc.size(), doc.size()) |
1072 | - c.Assert(batch, NotNil) |
1073 | - c.Check(batch.Messages, HasLen, doc.size()) |
1074 | -} |
1075 | - |
1076 | -func (s *S) TestBuildPushMessagesConsolidate(c *C) { |
1077 | - resp := &http.Response{ |
1078 | - StatusCode: http.StatusOK, |
1079 | - Body: closeWrapper{bytes.NewReader([]byte(inboxBody))}, |
1080 | - } |
1081 | - var state fbState |
1082 | - p := &fbPlugin{state: state, accountId: 32} |
1083 | - var doc inboxDoc |
1084 | - p.decodeResponse(resp, &doc) |
1085 | - notifications := p.filterNotifications(&doc, &p.state.LastInboxUpdate) |
1086 | - max := doc.size() - 2 |
1087 | - batch := p.buildPushMessages(notifications, &doc, max, max) |
1088 | - c.Assert(batch, NotNil) |
1089 | - // we should get all the messages in a batch with Limit=max |
1090 | - c.Check(batch.Messages, HasLen, len(notifications)) |
1091 | - c.Check(batch.Limit, Equals, max) |
1092 | -} |
1093 | - |
1094 | -func (s *S) TestStateFromStorageInitialState(c *C) { |
1095 | - state, err := stateFromStorage(32) |
1096 | - c.Check(err, NotNil) |
1097 | - c.Check(state.LastUpdate, Equals, timeStamp("")) |
1098 | - c.Check(state.LastInboxUpdate, Equals, timeStamp("")) |
1099 | -} |
1100 | - |
1101 | -func (s *S) TestStateFromStoragePersist(c *C) { |
1102 | - filePath := filepath.Join(s.tempDir, "facebook.test/facebook-32.json") |
1103 | - state, err := stateFromStorage(32) |
1104 | - c.Check(err, NotNil) |
1105 | - state.LastInboxUpdate = timeStamp("2014-08-25T18:39:32+0000") |
1106 | - state.LastUpdate = timeStamp("2014-08-25T18:39:33+0000") |
1107 | - err = state.persist(32) |
1108 | - c.Check(err, IsNil) |
1109 | - jsonData, err := ioutil.ReadFile(filePath) |
1110 | - c.Check(err, IsNil) |
1111 | - var data map[string]string |
1112 | - json.Unmarshal(jsonData, &data) |
1113 | - c.Check(data["last_inbox_update"], Equals, "2014-08-25T18:39:32+0000") |
1114 | - c.Check(data["last_notification_update"], Equals, "2014-08-25T18:39:33+0000") |
1115 | -} |
1116 | - |
1117 | -func (s *S) TestStateFromStoragePersistFails(c *C) { |
1118 | - state := fbState{LastInboxUpdate: "2014-08-25T18:39:32+0000", LastUpdate: "2014-08-25T18:39:33+0000"} |
1119 | - plugins.XdgDataEnsure = plugins.XdgDataFind |
1120 | - err := state.persist(32) |
1121 | - c.Check(err, NotNil) |
1122 | -} |
1123 | - |
1124 | -func (s *S) TestStateFromStorage(c *C) { |
1125 | - state := fbState{LastInboxUpdate: "2014-08-25T18:39:32+0000", LastUpdate: "2014-08-25T18:39:33+0000"} |
1126 | - err := state.persist(32) |
1127 | - c.Check(err, IsNil) |
1128 | - newState, err := stateFromStorage(32) |
1129 | - c.Check(err, IsNil) |
1130 | - c.Check(newState.LastUpdate, Equals, timeStamp("2014-08-25T18:39:33+0000")) |
1131 | - c.Check(newState.LastInboxUpdate, Equals, timeStamp("2014-08-25T18:39:32+0000")) |
1132 | - // bad format |
1133 | - state = fbState{LastInboxUpdate: "yesterday", LastUpdate: "2014-08-25T18:39:33+0000"} |
1134 | - state.persist(32) |
1135 | - _, err = stateFromStorage(32) |
1136 | - c.Check(err, NotNil) |
1137 | - state = fbState{LastInboxUpdate: "2014-08-25T18:39:33+0000", LastUpdate: "today"} |
1138 | - state.persist(32) |
1139 | - _, err = stateFromStorage(32) |
1140 | - c.Check(err, NotNil) |
1141 | -} |
1142 | - |
1143 | -func (s *S) TestNew(c *C) { |
1144 | - state := fbState{LastInboxUpdate: "2014-08-25T18:39:32+0000", LastUpdate: "2014-08-25T18:39:33+0000"} |
1145 | - state.persist(32) |
1146 | - p := New(32) |
1147 | - fb := p.(*fbPlugin) |
1148 | - c.Check(fb.state, DeepEquals, state) |
1149 | - // with bad format |
1150 | - state = fbState{LastInboxUpdate: "hola", LastUpdate: "mundo"} |
1151 | - state.persist(32) |
1152 | - p = New(32) |
1153 | - fb = p.(*fbPlugin) |
1154 | - c.Check(fb.state, DeepEquals, state) |
1155 | -} |
1156 | - |
1157 | -func (s *S) TestApplicationId(c *C) { |
1158 | - expected := plugins.ApplicationId("com.ubuntu.developer.webapps.webapp-facebook_webapp-facebook") |
1159 | - p := New(32) |
1160 | - c.Check(p.ApplicationId(), Equals, expected) |
1161 | -} |
1162 | - |
1163 | -func (s *S) TestThreadIsValid(c *C) { |
1164 | - t := thread{} |
1165 | - t.Unseen = 1 |
1166 | - t.Unread = 2 |
1167 | - // 10m before Now, the thread should be valid |
1168 | - t.UpdatedTime = timeStamp(time.Now().Add(-10 * time.Minute).Format(facebookTime)) |
1169 | - tStamp := timeStamp(time.Now().Add(-20 * time.Minute).Format(facebookTime)) |
1170 | - c.Check(t.isValid(tStamp), Equals, true) |
1171 | - // 2m before Now, the thread should be invalid |
1172 | - t.UpdatedTime = timeStamp(time.Now().Add(-2 * time.Minute).Format(facebookTime)) |
1173 | - c.Check(t.isValid(tStamp), Equals, false) |
1174 | - // unseen = 0 |
1175 | - t.Unseen = 0 |
1176 | - t.UpdatedTime = timeStamp(time.Now().Add(-10 * time.Minute).Format(facebookTime)) |
1177 | - c.Check(t.isValid(tStamp), Equals, false) |
1178 | - // unread = 0, unseen = 1 |
1179 | - t.Unread = 0 |
1180 | - t.Unseen = 1 |
1181 | - c.Check(t.isValid(tStamp), Equals, false) |
1182 | -} |
1183 | - |
1184 | -func (s *S) TestGetInbox(c *C) { |
1185 | - doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) { |
1186 | - return &http.Response{ |
1187 | - StatusCode: http.StatusOK, |
1188 | - Body: closeWrapper{bytes.NewReader([]byte(inboxBody))}, |
1189 | - }, nil |
1190 | - } |
1191 | - var state fbState |
1192 | - authData := accounts.AuthData{} |
1193 | - authData.AccessToken = "foo" |
1194 | - p := &fbPlugin{state: state, accountId: 32} |
1195 | - batch, err := p.getInbox(&authData) |
1196 | - c.Assert(err, IsNil) |
1197 | - c.Assert(batch, NotNil) |
1198 | - msgs := batch.Messages |
1199 | - c.Check(msgs, HasLen, 3) |
1200 | -} |
1201 | - |
1202 | -func (s *S) TestGetInboxRequestFails(c *C) { |
1203 | - doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) { |
1204 | - return &http.Response{ |
1205 | - StatusCode: http.StatusBadRequest, |
1206 | - Body: closeWrapper{bytes.NewReader([]byte(""))}, |
1207 | - }, fmt.Errorf("please, fail") |
1208 | - } |
1209 | - var state fbState |
1210 | - authData := accounts.AuthData{} |
1211 | - authData.AccessToken = "foo" |
1212 | - p := &fbPlugin{state: state, accountId: 32} |
1213 | - _, err := p.getInbox(&authData) |
1214 | - c.Check(err, NotNil) |
1215 | -} |
1216 | - |
1217 | -func (s *S) TestGetInboxParseResponseFails(c *C) { |
1218 | - doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) { |
1219 | - return &http.Response{ |
1220 | - StatusCode: http.StatusOK, |
1221 | - Body: closeWrapper{bytes.NewReader([]byte("hola" + inboxBody))}, |
1222 | - }, nil |
1223 | - } |
1224 | - var state fbState |
1225 | - authData := accounts.AuthData{} |
1226 | - authData.AccessToken = "foo" |
1227 | - p := &fbPlugin{state: state, accountId: 32} |
1228 | - _, err := p.getInbox(&authData) |
1229 | - c.Check(err, NotNil) |
1230 | -} |
1231 | - |
1232 | -func (s *S) TestGetNotifications(c *C) { |
1233 | - doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) { |
1234 | - return &http.Response{ |
1235 | - StatusCode: http.StatusOK, |
1236 | - Body: closeWrapper{bytes.NewReader([]byte(notificationsBody))}, |
1237 | - }, nil |
1238 | - } |
1239 | - var state fbState |
1240 | - authData := accounts.AuthData{} |
1241 | - authData.AccessToken = "foo" |
1242 | - p := &fbPlugin{state: state, accountId: 32} |
1243 | - batch, err := p.getNotifications(&authData) |
1244 | - c.Assert(err, IsNil) |
1245 | - c.Assert(batch, NotNil) |
1246 | - msgs := batch.Messages |
1247 | - c.Check(msgs, HasLen, 2) |
1248 | -} |
1249 | - |
1250 | -func (s *S) TestGetNotificationsRequestFails(c *C) { |
1251 | - doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) { |
1252 | - return &http.Response{ |
1253 | - StatusCode: http.StatusBadRequest, |
1254 | - Body: closeWrapper{bytes.NewReader([]byte(""))}, |
1255 | - }, fmt.Errorf("please, fail") |
1256 | - } |
1257 | - var state fbState |
1258 | - authData := accounts.AuthData{} |
1259 | - authData.AccessToken = "foo" |
1260 | - p := &fbPlugin{state: state, accountId: 32} |
1261 | - _, err := p.getNotifications(&authData) |
1262 | - c.Check(err, NotNil) |
1263 | -} |
1264 | - |
1265 | -func (s *S) TestGetNotificationsParseResponseFails(c *C) { |
1266 | - doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) { |
1267 | - return &http.Response{ |
1268 | - StatusCode: http.StatusOK, |
1269 | - Body: closeWrapper{bytes.NewReader([]byte("hola" + notificationsBody))}, |
1270 | - }, nil |
1271 | - } |
1272 | - var state fbState |
1273 | - authData := accounts.AuthData{} |
1274 | - authData.AccessToken = "foo" |
1275 | - p := &fbPlugin{state: state, accountId: 32} |
1276 | - _, err := p.getNotifications(&authData) |
1277 | - c.Check(err, NotNil) |
1278 | -} |
1279 | - |
1280 | -func (s *S) TestPoll(c *C) { |
1281 | - doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) { |
1282 | - body := inboxBody |
1283 | - if url == "me/notifications" { |
1284 | - body = notificationsBody |
1285 | - } |
1286 | - return &http.Response{ |
1287 | - StatusCode: http.StatusOK, |
1288 | - Body: closeWrapper{bytes.NewReader([]byte(body))}, |
1289 | - }, nil |
1290 | - } |
1291 | - var state fbState |
1292 | - authData := accounts.AuthData{} |
1293 | - authData.AccessToken = "foo" |
1294 | - p := &fbPlugin{state: state, accountId: 32} |
1295 | - batches, err := p.Poll(&authData) |
1296 | - c.Assert(err, IsNil) |
1297 | - c.Assert(batches, NotNil) |
1298 | - c.Assert(batches, HasLen, 2) |
1299 | - c.Assert(batches[0], NotNil) |
1300 | - c.Assert(batches[1], NotNil) |
1301 | - c.Check(batches[0].Tag, Equals, "notification") |
1302 | - c.Check(batches[0].Messages, HasLen, 2) |
1303 | - c.Check(batches[1].Tag, Equals, "inbox") |
1304 | - c.Check(batches[1].Messages, HasLen, 3) |
1305 | -} |
1306 | - |
1307 | -func (s *S) TestPollSingleError(c *C) { |
1308 | - doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) { |
1309 | - body := inboxBody |
1310 | - if url == "me/notifications" { |
1311 | - body = "hola" + notificationsBody |
1312 | - } |
1313 | - return &http.Response{ |
1314 | - StatusCode: http.StatusOK, |
1315 | - Body: closeWrapper{bytes.NewReader([]byte(body))}, |
1316 | - }, nil |
1317 | - } |
1318 | - var state fbState |
1319 | - authData := accounts.AuthData{} |
1320 | - authData.AccessToken = "foo" |
1321 | - p := &fbPlugin{state: state, accountId: 32} |
1322 | - batches, err := p.Poll(&authData) |
1323 | - c.Assert(err, IsNil) |
1324 | - c.Assert(batches, HasLen, 1) |
1325 | - c.Assert(batches[0], NotNil) |
1326 | - c.Check(batches[0].Messages, HasLen, 3) |
1327 | -} |
1328 | - |
1329 | -func (s *S) TestPollError(c *C) { |
1330 | - doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) { |
1331 | - body := "hola" + inboxBody |
1332 | - if url == "me/notifications" { |
1333 | - body = "hola" + notificationsBody |
1334 | - } |
1335 | - return &http.Response{ |
1336 | - StatusCode: http.StatusOK, |
1337 | - Body: closeWrapper{bytes.NewReader([]byte(body))}, |
1338 | - }, nil |
1339 | - } |
1340 | - var state fbState |
1341 | - authData := accounts.AuthData{} |
1342 | - authData.AccessToken = "foo" |
1343 | - p := &fbPlugin{state: state, accountId: 32} |
1344 | - _, err := p.Poll(&authData) |
1345 | - c.Check(err, NotNil) |
1346 | -} |
1347 | - |
1348 | -func (s *S) TestPollUseEnvToken(c *C) { |
1349 | - doRequest = func(a *accounts.AuthData, url string) (*http.Response, error) { |
1350 | - c.Check(a.AccessToken, Equals, "bar") |
1351 | - return nil, fmt.Errorf("please, fail") |
1352 | - } |
1353 | - var state fbState |
1354 | - authData := accounts.AuthData{} |
1355 | - authData.AccessToken = "foo" |
1356 | - os.Setenv("ACCOUNT_POLLD_TOKEN_FACEBOOK", "bar") |
1357 | - // defer the unset |
1358 | - defer os.Setenv("ACCOUNT_POLLD_TOKEN_FACEBOOK", "") |
1359 | - p := &fbPlugin{state: state, accountId: 32} |
1360 | - _, err := p.Poll(&authData) |
1361 | - c.Check(err, NotNil) |
1362 | -} |
1363 | - |
1364 | -func (s *S) TestRequest(c *C) { |
1365 | - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
1366 | - fmt.Fprintln(w, "Hello, client") |
1367 | - })) |
1368 | - defer ts.Close() |
1369 | - baseUrl, _ = url.Parse(ts.URL) |
1370 | - authData := accounts.AuthData{} |
1371 | - authData.AccessToken = "foo" |
1372 | - _, err := request(&authData, "me/notifications") |
1373 | - c.Check(err, IsNil) |
1374 | -} |
PASSED: Continuous integration, rev:138 jenkins. qa.ubuntu. com/job/ account- polld-ci/ 89/ jenkins. qa.ubuntu. com/job/ account- polld-vivid- amd64-ci/ 4 jenkins. qa.ubuntu. com/job/ account- polld-vivid- armhf-ci/ 4 jenkins. qa.ubuntu. com/job/ account- polld-vivid- armhf-ci/ 4/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ account- polld-vivid- i386-ci/ 4
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/account- polld-ci/ 89/rebuild
http://