diff --git a/cmd/reporter/app/cmd_event.go b/cmd/reporter/app/cmd_event.go index 44c33612..c2d9f07d 100644 --- a/cmd/reporter/app/cmd_event.go +++ b/cmd/reporter/app/cmd_event.go @@ -51,7 +51,12 @@ func eventCommandHandler(cmd *cobra.Command, args []string) error { } ctx := context.Background() - return tracker.Tracker.SendEvent(ctx, event) + + t, err := tracker.NewTracker() + if err != nil { + return err + } + return t.SendEvent(ctx, event) } //EventCommand reports an event to analytics diff --git a/cmd/reporter/main.go b/cmd/reporter/main.go index 7d157fc6..0f070e33 100644 --- a/cmd/reporter/main.go +++ b/cmd/reporter/main.go @@ -17,14 +17,18 @@ limitations under the License. package main import ( - "log" + "go.uber.org/zap" "github.com/fission/fission/cmd/reporter/app" + "github.com/fission/fission/pkg/utils/loggerfactory" ) func main() { + logger := loggerfactory.GetLogger() + defer logger.Sync() + err := app.App().Execute() if err != nil { - log.Fatal(err) + logger.Error("error occurred during analytics reporting", zap.Error(err)) } } diff --git a/pkg/tracker/tracker.go b/pkg/tracker/tracker.go index 9a9351ca..ed750935 100644 --- a/pkg/tracker/tracker.go +++ b/pkg/tracker/tracker.go @@ -19,6 +19,7 @@ import ( "bytes" "context" "errors" + "fmt" "net/http" "net/url" "os" @@ -27,16 +28,17 @@ import ( uuid "github.com/satori/go.uuid" ) -const HTTP_TIMEOUT = 5 * time.Second -const GA_API_URL = "https://www.google-analytics.com/collect" -const GA_TRACKING_ID = "GA_TRACKING_ID" - -var Tracker *tracker +const ( + HTTP_TIMEOUT time.Duration = 5 * time.Second + GA_TRACKING_ID string = "GA_TRACKING_ID" + GA_API_URL string = "GA_API_URL" +) type ( - tracker struct { + Tracker struct { gaPropertyID string cid string + gaAPIURL string } Event struct { Category string @@ -46,19 +48,31 @@ type ( } ) -func init() { +func NewTracker() (*Tracker, error) { id, err := uuid.NewV4() if err != nil { - panic(err) + return nil, fmt.Errorf("tracker.NewTracker: error generating UUID: %w", err) } - Tracker = &tracker{gaPropertyID: os.Getenv(GA_TRACKING_ID), cid: id.String()} + + gaTrackingID := os.Getenv(GA_TRACKING_ID) + if gaTrackingID == "" { + return nil, errors.New("tracker.NewTracker: GA_TRACKING_ID env not set") + } + + gaAPIURL := os.Getenv(GA_API_URL) + if gaAPIURL == "" { + gaAPIURL = "https://www.google-analytics.com/collect" + } + + tracker := &Tracker{ + gaPropertyID: gaTrackingID, + cid: id.String(), + gaAPIURL: gaAPIURL, + } + return tracker, nil } -func (t *tracker) SendEvent(ctx context.Context, e Event) error { - if t.gaPropertyID == "" { - return errors.New("tracker.SendEvent: GA_TRACKING_ID env not set") - } - +func (t *Tracker) SendEvent(ctx context.Context, e Event) error { if e.Action == "" || e.Category == "" { return errors.New("tracker.SendEvent: category and action are required") } @@ -81,7 +95,7 @@ func (t *tracker) SendEvent(ctx context.Context, e Event) error { } buf := bytes.NewBufferString(v.Encode()) - req, err := http.NewRequestWithContext(ctx, "POST", GA_API_URL, buf) + req, err := http.NewRequestWithContext(ctx, "POST", t.gaAPIURL, buf) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("User-Agent", "ga-tracker/1.0") if err != nil { diff --git a/pkg/tracker/tracker_test.go b/pkg/tracker/tracker_test.go new file mode 100644 index 00000000..0a04fefd --- /dev/null +++ b/pkg/tracker/tracker_test.go @@ -0,0 +1,127 @@ +package tracker + +import ( + "context" + "encoding/json" + "errors" + "log" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTracker(t *testing.T) { + t.Run("NewTracker", func(test *testing.T) { + for _, test := range []struct { + name string + gaAPIURl string + expected error + }{ + { + name: "GA Tracking ID should not be empty", + gaAPIURl: "", + expected: errors.New("tracker.NewTracker: GA_TRACKING_ID env not set"), + }, + { + name: "Tracker should initialize properly", + gaAPIURl: "/", + expected: nil, + }, + } { + t.Run(test.name, func(testing *testing.T) { + if test.expected == nil { + os.Setenv(GA_TRACKING_ID, "UA-000000-2") + t, err := NewTracker() + assert.Nil(testing, err) + assert.NotNil(testing, t) + } else { + t, err := NewTracker() + assert.Nil(testing, t) + assert.NotNil(testing, err) + assert.Equal(testing, err.Error(), test.expected.Error()) + } + }) + } + }) + + t.Run("SendEvent", func(test *testing.T) { + + os.Setenv(GA_TRACKING_ID, "UA-000000-2") + tr, err := NewTracker() + if err != nil { + log.Fatal(err) + } + for _, test := range []struct { + name string + request *Event + expected error + status int + }{ + { + name: "category and action should not be empty", + request: &Event{ + Category: "", + Action: "", + Label: "play", + Value: "value", + }, + expected: errors.New("tracker.SendEvent: category and action are required"), + status: http.StatusInternalServerError, + }, + { + name: "Google Analytics response should not be OK", + request: &Event{ + Category: "UI_action", + Action: "button_press", + Label: "play", + Value: "value", + }, + + expected: errors.New("tracker.SendEvent: analytics response status not ok"), + status: http.StatusInternalServerError, + }, + { + name: "Google Analytics response should be OK", + request: &Event{ + Category: "UI_action", + Action: "button_press", + Label: "play", + Value: "value", + }, + + expected: nil, + status: http.StatusOK, + }, + } { + t.Run(test.name, func(testing *testing.T) { + server := MockHTTPServer(test.status, "") + defer server.Close() + tr.gaAPIURL = server.URL + + t := tr.SendEvent(context.Background(), *test.request) + if test.status == http.StatusOK { + assert.Nil(testing, t, test.expected) + } else { + assert.NotNil(testing, t) + assert.Equal(testing, t.Error(), test.expected.Error()) + } + + }) + } + }) +} + +func MockHTTPServer(status int, encodeValue interface{}) *httptest.Server { + f := func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(status) + w.Header().Set("Content-Type", "application/json") + err := json.NewEncoder(w).Encode(encodeValue) + if err != nil { + log.Fatal(err) + } + } + return httptest.NewServer(http.HandlerFunc(f)) +}