github.com/Financial-Times/publish-availability-monitor@v1.12.0/feeds/notificationsPullFeed.go (about) 1 package feeds 2 3 import ( 4 "encoding/json" 5 "net/url" 6 "sync" 7 "time" 8 9 "github.com/Financial-Times/go-logger/v2" 10 "github.com/Financial-Times/publish-availability-monitor/httpcaller" 11 ) 12 13 const NotificationsPull = "Notifications-Pull" 14 15 type NotificationsPullFeed struct { 16 baseNotificationsFeed 17 notificationsURL string 18 notificationsQueryString string 19 notificationsURLLock *sync.Mutex 20 interval int 21 ticker *time.Ticker 22 poller chan struct{} 23 log *logger.UPPLogger 24 } 25 26 // ignore unused field (e.g. requestUrl) 27 type notificationsResponse struct { 28 Notifications []Notification 29 Links []Link 30 } 31 32 func (f *NotificationsPullFeed) Start() { 33 if f.httpCaller == nil { 34 f.httpCaller = httpcaller.NewCaller(10) 35 } 36 37 f.ticker = time.NewTicker(time.Duration(f.interval) * time.Second) 38 f.poller = make(chan struct{}) 39 go func() { 40 for { 41 select { 42 case <-f.ticker.C: 43 go func() { 44 f.pollNotificationsFeed() 45 f.purgeObsoleteNotifications() 46 }() 47 case <-f.poller: 48 f.ticker.Stop() 49 return 50 } 51 } 52 }() 53 } 54 55 func (f *NotificationsPullFeed) Stop() { 56 f.log.Infof("shutting down notifications pull feed for %s", f.baseURL) 57 close(f.poller) 58 } 59 60 func (f *NotificationsPullFeed) FeedType() string { 61 return NotificationsPull 62 } 63 64 func (f *NotificationsPullFeed) pollNotificationsFeed() { 65 f.notificationsURLLock.Lock() 66 defer f.notificationsURLLock.Unlock() 67 68 tid := f.buildNotificationsTID() 69 log := f.log.WithTransactionID(tid) 70 notificationsURL := f.notificationsURL + "?" + f.notificationsQueryString 71 72 resp, err := f.httpCaller.DoCall(httpcaller.Config{ 73 URL: notificationsURL, 74 Username: f.username, 75 Password: f.password, 76 TID: tid, 77 }) 78 if err != nil { 79 log.WithError(err).Errorf("error calling notifications %s", notificationsURL) 80 return 81 } 82 defer func() { 83 _ = resp.Body.Close() 84 }() 85 86 if resp.StatusCode != 200 { 87 log.Errorf("Notifications [%s] status NOT OK: [%d]", notificationsURL, resp.StatusCode) 88 return 89 } 90 91 var notifications notificationsResponse 92 err = json.NewDecoder(resp.Body).Decode(¬ifications) 93 if err != nil { 94 log.WithError(err).Error("Cannot decode json response") 95 return 96 } 97 98 f.notificationsLock.Lock() 99 defer f.notificationsLock.Unlock() 100 101 for _, v := range notifications.Notifications { 102 n := v 103 uuid := parseUUIDFromURL(n.ID) 104 var history []*Notification 105 var found bool 106 if history, found = f.notifications[uuid]; !found { 107 history = make([]*Notification, 0) 108 } 109 110 history = append(history, &n) 111 f.notifications[uuid] = history 112 } 113 114 nextPageURL, err := url.Parse(notifications.Links[0].Href) 115 if err != nil { 116 log.WithError(err).Errorf("unparseable next url: [%s]", notifications.Links[0].Href) 117 return // and hope that a retry will fix this 118 } 119 120 f.notificationsQueryString = nextPageURL.RawQuery 121 } 122 123 func (f *NotificationsPullFeed) buildNotificationsTID() string { 124 return "tid_pam_notifications_pull_" + time.Now().Format(time.RFC3339) 125 }