diff --git a/plugins/all/all.go b/plugins/all/all.go index ba0859f65993b..676d257077963 100644 --- a/plugins/all/all.go +++ b/plugins/all/all.go @@ -13,6 +13,7 @@ import ( _ "github.com/influxdb/telegraf/plugins/kafka_consumer" _ "github.com/influxdb/telegraf/plugins/leofs" _ "github.com/influxdb/telegraf/plugins/lustre2" + _ "github.com/influxdb/telegraf/plugins/mailchimp" _ "github.com/influxdb/telegraf/plugins/memcached" _ "github.com/influxdb/telegraf/plugins/mongodb" _ "github.com/influxdb/telegraf/plugins/mysql" diff --git a/plugins/apache/apache_test.go b/plugins/apache/apache_test.go index 0185428841577..9688302ac2c40 100644 --- a/plugins/apache/apache_test.go +++ b/plugins/apache/apache_test.go @@ -29,7 +29,7 @@ ConnsAsyncClosing: 205 Scoreboard: WW_____W_RW_R_W__RRR____WR_W___WW________W_WW_W_____R__R_WR__WRWR_RRRW___R_RWW__WWWRW__R_RW___RR_RW_R__W__WR_WWW______WWR__R___R_WR_W___RW______RR________________W______R__RR______W________________R____R__________________________RW_W____R_____W_R_________________R____RR__W___R_R____RW______R____W______W_W_R_R______R__R_R__________R____W_______WW____W____RR__W_____W_R_______W__________W___W____________W_______WRR_R_W____W_____R____W_WW_R____RRW__W............................................................................................................................................................................................................................................................................................................WRRWR____WR__RR_R___RWR_________W_R____RWRRR____R_R__RW_R___WWW_RW__WR_RRR____W___R____WW_R__R___RR_W_W_RRRRWR__RRWR__RRW_W_RRRW_R_RR_W__RR_RWRR_R__R___RR_RR______R__RR____R_____W_R_R_R__R__R__________W____WW_R___R_R___R_________RR__RR____RWWWW___W_R________R_R____R_W___W___R___W_WRRWW_______R__W_RW_______R________RR__R________W_______________________W_W______________RW_________WR__R___R__R_______________WR_R_________W___RW_____R____________W____...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... ` -func TestHTTPInflux(t *testing.T) { +func TestHTTPApache(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintln(w, apacheStatus) diff --git a/plugins/mailchimp/chimp_api.go b/plugins/mailchimp/chimp_api.go new file mode 100644 index 0000000000000..a1d1ebe456293 --- /dev/null +++ b/plugins/mailchimp/chimp_api.go @@ -0,0 +1,173 @@ +package mailchimp + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" + "regexp" +) + +const ( + reports_summary_endpoint string = "/3.0/reports" +) + +var mailchimp_datacenter = regexp.MustCompile("[a-z]+[0-9]+$") + +type ChimpAPI struct { + Transport http.RoundTripper + endpoint string + Debug bool +} + +func NewChimp(apiKey string) *ChimpAPI { + u := url.URL{} + u.Scheme = "https" + u.Host = fmt.Sprintf("%s.api.mailchimp.com", mailchimp_datacenter.FindString(apiKey)) + u.Path = reports_summary_endpoint + u.User = url.UserPassword("", apiKey) + return &ChimpAPI{endpoint: u.String()} +} + +type APIError struct { + Status int `json:"status"` + Type string `json:"type"` + Title string `json:"title"` + Detail string `json:"detail"` + Instance string `json:"instance"` +} + +func (e APIError) Error() string { + return fmt.Sprintf("ERROR %v: %v. See %v", e.Status, e.Title, e.Type) +} + +func chimpErrorCheck(body []byte) error { + var e APIError + json.Unmarshal(body, &e) + if e.Title != "" || e.Status != 0 { + return e + } + return nil +} + +func (a *ChimpAPI) GetReports() (ReportsResponse, error) { + var response ReportsResponse + rawjson, err := runChimp(a) + if err != nil { + return response, err + } + + err = json.Unmarshal(rawjson, &response) + if err != nil { + return response, err + } + + return response, nil +} + +func runChimp(api *ChimpAPI) ([]byte, error) { + if api.Debug { + log.Printf("Request URL: %s", api.endpoint) + } + client := &http.Client{Transport: api.Transport} + resp, err := client.Get(api.endpoint) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if api.Debug { + log.Printf("Response Body:%s", string(body)) + } + + if err = chimpErrorCheck(body); err != nil { + return nil, err + } + return body, nil +} + +type ReportsResponse struct { + Reports []Report `json:"reports"` + TotalItems int `json:"total_items"` +} + +type Report struct { + ID string `json:"id"` + CampaignTitle string `json:"campaign_title"` + Type string `json:"type"` + EmailsSent int `json:"emails_sent"` + AbuseReports int `json:"abuse_reports"` + Unsubscribed int `json:"unsubscribed"` + SendTime string `json:"send_time"` + + TimeSeries []TimeSerie + Bounces Bounces `json:"bounces"` + Forwards Forwards `json:"forwards"` + Opens Opens `json:"opens"` + Clicks Clicks `json:"clicks"` + FacebookLikes FacebookLikes `json:"facebook_likes"` + IndustryStats IndustryStats `json:"industry_stats"` + ListStats ListStats `json:"list_stats"` +} + +type Bounces struct { + HardBounces int `json:"hard_bounces"` + SoftBounces int `json:"soft_bounces"` + SyntaxErrors int `json:"syntax_errors"` +} + +type Forwards struct { + ForwardsCount int `json:"forwards_count"` + ForwardsOpens int `json:"forwards_opens"` +} + +type Opens struct { + OpensTotal int `json:"opens_total"` + UniqueOpens int `json:"unique_opens"` + OpenRate float64 `json:"open_rate"` + LastOpen string `json:"last_open"` +} + +type Clicks struct { + ClicksTotal int `json:"clicks_total"` + UniqueClicks int `json:"unique_clicks"` + UniqueSubscriberClicks int `json:"unique_subscriber_clicks"` + ClickRate float64 `json:"click_rate"` + LastClick string `json:"last_click"` +} + +type FacebookLikes struct { + RecipientLikes int `json:"recipient_likes"` + UniqueLikes int `json:"unique_likes"` + FacebookLikes int `json:"facebook_likes"` +} + +type IndustryStats struct { + Type string `json:"type"` + OpenRate float64 `json:"open_rate"` + ClickRate float64 `json:"click_rate"` + BounceRate float64 `json:"bounce_rate"` + UnopenRate float64 `json:"unopen_rate"` + UnsubRate float64 `json:"unsub_rate"` + AbuseRate float64 `json:"abuse_rate"` +} + +type ListStats struct { + SubRate float64 `json:"sub_rate"` + UnsubRate float64 `json:"unsub_rate"` + OpenRate float64 `json:"open_rate"` + ClickRate float64 `json:"click_rate"` +} + +type TimeSerie struct { + TimeStamp string `json:"timestamp"` + EmailsSent int `json:"emails_sent"` + UniqueOpens int `json:"unique_opens"` + RecipientsClick int `json:"recipients_click"` +} diff --git a/plugins/mailchimp/mailchimp.go b/plugins/mailchimp/mailchimp.go new file mode 100644 index 0000000000000..ba0c09d43f060 --- /dev/null +++ b/plugins/mailchimp/mailchimp.go @@ -0,0 +1,86 @@ +package mailchimp + +import ( + "time" + + "github.com/influxdb/telegraf/plugins" +) + +type MailChimp struct { + api *ChimpAPI + + ApiKey string +} + +var sampleConfig = ` + # MailChimp API key + api_key = "" +` + +func (m *MailChimp) SampleConfig() string { + return sampleConfig +} + +func (m *MailChimp) Description() string { + return "Gathers metrics from the /reports MailChimp API" +} + +func (m *MailChimp) Gather(acc plugins.Accumulator) error { + if m.api == nil { + m.api = NewChimp(m.ApiKey) + } + m.api.Debug = false + + reports, err := m.api.GetReports() + if err != nil { + return err + } + now := time.Now() + + for _, report := range reports.Reports { + tags := make(map[string]string) + tags["id"] = report.ID + tags["campaign_title"] = report.CampaignTitle + acc.Add("emails_sent", report.EmailsSent, tags, now) + acc.Add("abuse_reports", report.AbuseReports, tags, now) + acc.Add("unsubscribed", report.Unsubscribed, tags, now) + acc.Add("hard_bounces", report.Bounces.HardBounces, tags, now) + acc.Add("soft_bounces", report.Bounces.SoftBounces, tags, now) + acc.Add("syntax_errors", report.Bounces.SyntaxErrors, tags, now) + acc.Add("forwards_count", report.Forwards.ForwardsCount, tags, now) + acc.Add("forwards_opens", report.Forwards.ForwardsOpens, tags, now) + acc.Add("opens_total", report.Opens.OpensTotal, tags, now) + acc.Add("unique_opens", report.Opens.UniqueOpens, tags, now) + acc.Add("open_rate", report.Opens.OpenRate, tags, now) + acc.Add("last_open", report.Opens.LastOpen, tags, now) + acc.Add("clicks_total", report.Clicks.ClicksTotal, tags, now) + acc.Add("unique_clicks", report.Clicks.UniqueClicks, tags, now) + acc.Add("unique_subscriber_clicks", report.Clicks.UniqueSubscriberClicks, tags, now) + acc.Add("click_rate", report.Clicks.ClickRate, tags, now) + acc.Add("last_click", report.Clicks.LastClick, tags, now) + acc.Add("facebook_recipient_likes", report.FacebookLikes.RecipientLikes, tags, now) + acc.Add("facebook_unique_likes", report.FacebookLikes.UniqueLikes, tags, now) + acc.Add("facebook_likes", report.FacebookLikes.FacebookLikes, tags, now) + acc.Add("industry_type", report.IndustryStats.Type, tags, now) + acc.Add("industry_open_rate", report.IndustryStats.OpenRate, tags, now) + acc.Add("industry_click_rate", report.IndustryStats.ClickRate, tags, now) + acc.Add("industry_bounce_rate", report.IndustryStats.BounceRate, tags, now) + acc.Add("industry_unopen_rate", report.IndustryStats.UnopenRate, tags, now) + acc.Add("industry_unsub_rate", report.IndustryStats.UnsubRate, tags, now) + acc.Add("industry_abuse_rate", report.IndustryStats.AbuseRate, tags, now) + acc.Add("list_stats_sub_rate", report.ListStats.SubRate, tags, now) + acc.Add("list_stats_unsub_rate", report.ListStats.UnsubRate, tags, now) + acc.Add("list_stats_open_rate", report.ListStats.OpenRate, tags, now) + acc.Add("list_stats_click_rate", report.ListStats.ClickRate, tags, now) + + // TODO(cam) parse and add TimeSeries data here too + } + + return nil +} + +func init() { + plugins.Add("mailchimp", func() plugins.Plugin { + return &MailChimp{} + }) +} diff --git a/plugins/mailchimp/mailchimp_test.go b/plugins/mailchimp/mailchimp_test.go new file mode 100644 index 0000000000000..db05ba9187f16 --- /dev/null +++ b/plugins/mailchimp/mailchimp_test.go @@ -0,0 +1,342 @@ +package mailchimp + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/influxdb/telegraf/testutil" + + //"github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestHTTPMailChimp(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + fmt.Fprintln(w, sampleResponse) + })) + defer ts.Close() + + api := &ChimpAPI{ + endpoint: ts.URL, + } + m := MailChimp{ + api: api, + } + + var acc testutil.Accumulator + err := m.Gather(&acc) + require.NoError(t, err) + + // TODO(cam) validate all stats from the sample response below. +} + +var sampleResponse = ` +{ + "reports": [ + { + "id": "42694e9e57", + "campaign_title": "Freddie's Jokes Vol. 1", + "type": "regular", + "emails_sent": 200, + "abuse_reports": 0, + "unsubscribed": 2, + "send_time": "2015-09-15T19:05:51+00:00", + "bounces": { + "hard_bounces": 0, + "soft_bounces": 2, + "syntax_errors": 0 + }, + "forwards": { + "forwards_count": 0, + "forwards_opens": 0 + }, + "opens": { + "opens_total": 186, + "unique_opens": 100, + "open_rate": 42, + "last_open": "2015-09-15T19:15:47+00:00" + }, + "clicks": { + "clicks_total": 42, + "unique_clicks": 400, + "unique_subscriber_clicks": 42, + "click_rate": 42, + "last_click": "2015-09-15T19:15:47+00:00" + }, + "facebook_likes": { + "recipient_likes": 5, + "unique_likes": 8, + "facebook_likes": 42 + }, + "industry_stats": { + "type": "Social Networks and Online Communities", + "open_rate": 0.17076777144396, + "click_rate": 0.027431311866951, + "bounce_rate": 0.0063767751251474, + "unopen_rate": 0.82285545343089, + "unsub_rate": 0.001436957032815, + "abuse_rate": 0.00021111996110887 + }, + "list_stats": { + "sub_rate": 10, + "unsub_rate": 20, + "open_rate": 42, + "click_rate": 42 + }, + "timeseries": [ + { + "timestamp": "2015-09-15T19:00:00+00:00", + "emails_sent": 198, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-15T20:00:00+00:00", + "emails_sent": 2, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-15T21:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-15T22:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-15T23:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T00:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T01:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T02:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T03:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T04:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T05:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T06:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T07:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T08:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T09:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T10:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T11:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T12:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T13:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T14:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T15:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T16:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T17:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + }, + { + "timestamp": "2015-09-16T18:00:00+00:00", + "emails_sent": 0, + "unique_opens": 0, + "recipients_clicks": 0 + } + ], + "share_report": { + "share_url": "http://usX.vip-reports.net/reports/summary?u=xxxx&id=xxxx", + "share_password": "freddielikesjokes" + }, + "delivery_status": { + "enabled": false + }, + "_links": [ + { + "rel": "parent", + "href": "https://usX.api.mailchimp.com/3.0/reports", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Reports/Collection.json", + "schema": "https://api.mailchimp.com/schema/3.0/CollectionLinks/Reports.json" + }, + { + "rel": "self", + "href": "https://usX.api.mailchimp.com/3.0/reports/42694e9e57", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Reports/Instance.json" + }, + { + "rel": "campaign", + "href": "https://usX.api.mailchimp.com/3.0/campaigns/42694e9e57", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Campaigns/Instance.json" + }, + { + "rel": "sub-reports", + "href": "https://usX.api.mailchimp.com/3.0/reports/42694e9e57/sub-reports", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Reports/Sub/Collection.json" + }, + { + "rel": "abuse-reports", + "href": "https://usX.api.mailchimp.com/3.0/reports/42694e9e57/abuse-reports", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Reports/Abuse/Collection.json" + }, + { + "rel": "advice", + "href": "https://usX.api.mailchimp.com/3.0/reports/42694e9e57/advice", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Reports/Advice/Collection.json" + }, + { + "rel": "click-details", + "href": "https://usX.api.mailchimp.com/3.0/reports/42694e9e57/click-details", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Reports/ClickDetails/Collection.json" + }, + { + "rel": "domain-performance", + "href": "https://usX.api.mailchimp.com/3.0/reports/42694e9e57/domain-performance", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Reports/DomainPerformance/Collection.json" + }, + { + "rel": "eepurl", + "href": "https://usX.api.mailchimp.com/3.0/reports/42694e9e57/eepurl", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Reports/Eepurl/Collection.json" + }, + { + "rel": "email-activity", + "href": "https://usX.api.mailchimp.com/3.0/reports/42694e9e57/email-activity", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Reports/EmailActivity/Collection.json" + }, + { + "rel": "locations", + "href": "https://usX.api.mailchimp.com/3.0/reports/42694e9e57/locations", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Reports/Locations/Collection.json" + }, + { + "rel": "sent-to", + "href": "https://usX.api.mailchimp.com/3.0/reports/42694e9e57/sent-to", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Reports/SentTo/Collection.json" + }, + { + "rel": "unsubscribed", + "href": "https://usX.api.mailchimp.com/3.0/reports/42694e9e57/unsubscribed", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Reports/Unsubs/Collection.json" + } + ] + } + ], + "_links": [ + { + "rel": "parent", + "href": "https://usX.api.mailchimp.com/3.0/", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Root.json" + }, + { + "rel": "self", + "href": "https://usX.api.mailchimp.com/3.0/reports", + "method": "GET", + "targetSchema": "https://api.mailchimp.com/schema/3.0/Reports/Collection.json", + "schema": "https://api.mailchimp.com/schema/3.0/CollectionLinks/Reports.json" + } + ], + "total_items": 1 +} +`