Skip to content
This repository has been archived by the owner on Jun 6, 2023. It is now read-only.

Commit

Permalink
Merge pull request #15 from RobotsAndPencils/timestamp
Browse files Browse the repository at this point in the history
parse and return timestamp in error struct
  • Loading branch information
curtisallen committed Jan 12, 2016
2 parents b70e6fa + 7bc2a3a commit c49dcf0
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 27 deletions.
67 changes: 40 additions & 27 deletions push/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ var (
ErrUnknown = errors.New("unknown error")
)

// Error with a timestamp
type Error struct {
Err error
Timestamp time.Time
DeviceToken string
}

func (e *Error) Error() string {
return fmt.Sprintf("%v (device token %v last invalid at %v)", e.Err.Error(), e.DeviceToken, e.Timestamp)
}

var errorReason = map[string]error{
"PayloadEmpty": ErrPayloadEmpty,
"PayloadTooLarge": ErrPayloadTooLarge,
Expand All @@ -122,11 +133,22 @@ var errorReason = map[string]error{
"MissingTopic": ErrMissingTopic,
}

var errorStatus = map[int]error{
http.StatusBadRequest: ErrBadRequest,
http.StatusForbidden: ErrForbidden,
http.StatusMethodNotAllowed: ErrMethodNotAllowed,
http.StatusGone: ErrGone,
http.StatusRequestEntityTooLarge: ErrPayloadTooLarge,
statusTooManyRequests: ErrTooManyRequests,
http.StatusInternalServerError: ErrInternalServerError,
http.StatusServiceUnavailable: ErrServiceUnavailable,
}

type response struct {
// Reason for failure
Reason string `json:"reason"`
// Timestamp for 410 errors (maybe this is an int)
Timestamp string `json:"timestamp"`
// Timestamp for 410 StatusGone (ErrUnregistered)
Timestamp int64 `json:"timestamp"`
}

const statusTooManyRequests = 429
Expand Down Expand Up @@ -171,35 +193,26 @@ func (s *Service) PushBytes(deviceToken string, headers *Headers, payload []byte
var response response
json.Unmarshal(body, &response)

if e, ok := errorReason[response.Reason]; ok {
return "", e
e, ok := errorReason[response.Reason]
if !ok {
// fallback to HTTP status codes if reason not found in JSON
e, ok = errorStatus[resp.StatusCode]
if !ok {
e = ErrUnknown
}
}

// fallback to HTTP status codes if reason not found in JSON

switch resp.StatusCode {
case http.StatusBadRequest:
return "", ErrBadRequest
case http.StatusForbidden:
return "", ErrForbidden
case http.StatusMethodNotAllowed:
return "", ErrMethodNotAllowed
case http.StatusGone:
// TODO: this should return an error structure with timestamp
// but I don't know the format of timestamp (Unix time?)
// and there may be a JSON response handled above (ErrUnregistered?)
return "", ErrGone
case http.StatusRequestEntityTooLarge:
return "", ErrPayloadTooLarge
case statusTooManyRequests:
return "", ErrTooManyRequests
case http.StatusInternalServerError:
return "", ErrInternalServerError
case http.StatusServiceUnavailable:
return "", ErrServiceUnavailable
// error constant if there was no timestamp
if response.Timestamp == 0 {
return "", e
}

return "", ErrUnknown
// error struct with a timestamp
return "", &Error{
Err: e,
Timestamp: time.Unix(response.Timestamp, 0),
DeviceToken: deviceToken,
}
}

// set headers for an HTTP request
Expand Down
43 changes: 43 additions & 0 deletions push/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/http/httptest"
"reflect"
"testing"
"time"

"github.com/RobotsAndPencils/buford/push"
)
Expand Down Expand Up @@ -72,3 +73,45 @@ func TestBadPriorityPush(t *testing.T) {
t.Errorf("Expected error %v, got %v.", push.ErrBadPriority, err)
}
}

func TestTimestampError(t *testing.T) {
deviceToken := "c2732227a1d8021cfaf781d71fb2f908c61f5861079a00954a5453f1d0281433"
payload := []byte(`{ "aps" : { "alert" : "Hello HTTP/2" } }`)

handler := http.NewServeMux()
server := httptest.NewServer(handler)

handler.HandleFunc("/3/device/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusGone)
w.Write([]byte(`{"reason":"Unregistered","timestamp":12622780800}`))
})

service := push.Service{
Client: http.DefaultClient,
Host: server.URL,
}

_, err := service.PushBytes(deviceToken, nil, payload)

if err == push.ErrUnregistered {
t.Error("Expected error structure, got constant.")
}

e, ok := err.(*push.Error)
if !ok {
t.Fatalf("Expected push error, got %v.", err)
}

if e.Err != push.ErrUnregistered {
t.Errorf("Expected error %v, got %v.", push.ErrUnregistered, err)
}

expected := time.Unix(12622780800, 0)
if e.Timestamp != expected {
t.Errorf("Expected timestamp %v, got %v.", expected, e.Timestamp)
}

if e.DeviceToken != deviceToken {
t.Errorf("Expected device token %v, got %v.", deviceToken, e.DeviceToken)
}
}

0 comments on commit c49dcf0

Please sign in to comment.