Merge pull request #36 from appleboy/multiple

Fix #35 support multiple notifications.
This commit is contained in:
Bo-Yi Wu 2016-04-09 23:15:53 -05:00
commit 91099ff74e
6 changed files with 204 additions and 151 deletions

View File

@ -17,5 +17,8 @@ docker_build: clean
tar -zxvf bin.tar.gz tar -zxvf bin.tar.gz
-rm -rf bin.tar.gz build.tar.gz -rm -rf bin.tar.gz build.tar.gz
test:
cd gopush && go test -v -covermode=count -coverprofile=coverage.out
clean: clean:
rm -rf build.tar.gz bin.tar.gz bin/* rm -rf build.tar.gz bin.tar.gz bin/*

View File

@ -174,7 +174,7 @@ func typeForPlatForm(platform int) string {
} }
} }
func LogPush(status, token string, req RequestPushNotification, errPush error) { func LogPush(status, token string, req PushNotification, errPush error) {
var plat, platColor, output string var plat, platColor, output string
platColor = colorForPlatForm(req.Platform) platColor = colorForPlatForm(req.Platform)

View File

@ -41,7 +41,11 @@ type Alert struct {
TitleLocKey string `json:"title-loc-key,omitempty"` TitleLocKey string `json:"title-loc-key,omitempty"`
} }
type RequestPushNotification struct { type RequestPush struct {
Notifications []PushNotification `json:"notifications" binding:"required"`
}
type PushNotification struct {
// Common // Common
Tokens []string `json:"tokens" binding:"required"` Tokens []string `json:"tokens" binding:"required"`
Platform int `json:"platform" binding:"required"` Platform int `json:"platform" binding:"required"`
@ -114,26 +118,33 @@ func InitAPNSClient() error {
return nil return nil
} }
func pushNotification(notification RequestPushNotification) bool { func SendNotification(req RequestPush) int {
switch notification.Platform { var count int
case PlatFormIos: for _, notification := range req.Notifications {
if !PushConf.Ios.Enabled { switch notification.Platform {
return false case PlatFormIos:
if !PushConf.Ios.Enabled {
continue
}
count += 1
go PushToIOS(notification)
case PlatFormAndroid:
if !PushConf.Android.Enabled {
continue
}
count += 1
go PushToAndroid(notification)
} }
go PushToIOS(notification)
case PlatFormAndroid:
if !PushConf.Android.Enabled {
return false
}
go PushToAndroid(notification)
} }
return true return count
} }
// The iOS Notification Payload // The iOS Notification Payload
// ref: https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/TheNotificationPayload.html // ref: https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/TheNotificationPayload.html
func GetIOSNotification(req RequestPushNotification) *apns.Notification { func GetIOSNotification(req PushNotification) *apns.Notification {
notification := &apns.Notification{} notification := &apns.Notification{}
if len(req.ApnsID) > 0 { if len(req.ApnsID) > 0 {
@ -225,7 +236,7 @@ func GetIOSNotification(req RequestPushNotification) *apns.Notification {
return notification return notification
} }
func PushToIOS(req RequestPushNotification) bool { func PushToIOS(req PushNotification) bool {
notification := GetIOSNotification(req) notification := GetIOSNotification(req)
@ -260,7 +271,7 @@ func PushToIOS(req RequestPushNotification) bool {
// HTTP Connection Server Reference for Android // HTTP Connection Server Reference for Android
// https://developers.google.com/cloud-messaging/http-server-ref // https://developers.google.com/cloud-messaging/http-server-ref
func GetAndroidNotification(req RequestPushNotification) gcm.HttpMessage { func GetAndroidNotification(req PushNotification) gcm.HttpMessage {
notification := gcm.HttpMessage{} notification := gcm.HttpMessage{}
notification.RegistrationIds = req.Tokens notification.RegistrationIds = req.Tokens
@ -311,7 +322,7 @@ func GetAndroidNotification(req RequestPushNotification) gcm.HttpMessage {
return notification return notification
} }
func PushToAndroid(req RequestPushNotification) bool { func PushToAndroid(req PushNotification) bool {
var apiKey string var apiKey string
notification := GetAndroidNotification(req) notification := GetAndroidNotification(req)

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"github.com/buger/jsonparser" "github.com/buger/jsonparser"
"github.com/google/go-gcm" "github.com/google/go-gcm"
"github.com/sideshow/apns2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"log" "log"
"os" "os"
@ -64,7 +65,7 @@ func TestIOSNotificationStructure(t *testing.T) {
test := "test" test := "test"
message := "Welcome notification Server" message := "Welcome notification Server"
req := RequestPushNotification{ req := PushNotification{
ApnsID: test, ApnsID: test,
Topic: test, Topic: test,
Expiration: time.Now().Unix(), Expiration: time.Now().Unix(),
@ -126,7 +127,7 @@ func TestIOSAlertNotificationStructure(t *testing.T) {
var dat map[string]interface{} var dat map[string]interface{}
test := "test" test := "test"
req := RequestPushNotification{ req := PushNotification{
Alert: Alert{ Alert: Alert{
Action: test, Action: test,
ActionLocKey: test, ActionLocKey: test,
@ -178,7 +179,7 @@ func TestIOSAlertNotificationStructure(t *testing.T) {
func TestAndroidNotificationStructure(t *testing.T) { func TestAndroidNotificationStructure(t *testing.T) {
test := "test" test := "test"
req := RequestPushNotification{ req := PushNotification{
Tokens: []string{"a", "b"}, Tokens: []string{"a", "b"},
Message: "Welcome", Message: "Welcome",
To: test, To: test,
@ -219,7 +220,7 @@ func TestPushToIOS(t *testing.T) {
PushConf.Ios.PemKeyPath = "../certificate/certificate-valid.pem" PushConf.Ios.PemKeyPath = "../certificate/certificate-valid.pem"
InitAPNSClient() InitAPNSClient()
req := RequestPushNotification{ req := PushNotification{
Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"}, Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"},
Platform: 1, Platform: 1,
Message: "Welcome", Message: "Welcome",
@ -235,7 +236,7 @@ func TestPushToAndroidWrongAPIKey(t *testing.T) {
PushConf.Android.Enabled = true PushConf.Android.Enabled = true
PushConf.Android.ApiKey = os.Getenv("ANDROID_API_KEY") + "a" PushConf.Android.ApiKey = os.Getenv("ANDROID_API_KEY") + "a"
req := RequestPushNotification{ req := PushNotification{
Tokens: []string{"aaaaaa", "bbbbb"}, Tokens: []string{"aaaaaa", "bbbbb"},
Platform: 2, Platform: 2,
Message: "Welcome", Message: "Welcome",
@ -251,7 +252,7 @@ func TestPushToAndroidWrongToken(t *testing.T) {
PushConf.Android.Enabled = true PushConf.Android.Enabled = true
PushConf.Android.ApiKey = os.Getenv("ANDROID_API_KEY") PushConf.Android.ApiKey = os.Getenv("ANDROID_API_KEY")
req := RequestPushNotification{ req := PushNotification{
Tokens: []string{"aaaaaa", "bbbbb"}, Tokens: []string{"aaaaaa", "bbbbb"},
Platform: 2, Platform: 2,
Message: "Welcome", Message: "Welcome",
@ -271,7 +272,7 @@ func TestPushToAndroidRightTokenForJSONLog(t *testing.T) {
android_token := os.Getenv("ANDROID_TEST_TOKEN") android_token := os.Getenv("ANDROID_TEST_TOKEN")
req := RequestPushNotification{ req := PushNotification{
Tokens: []string{android_token, "bbbbb"}, Tokens: []string{android_token, "bbbbb"},
Platform: 2, Platform: 2,
Message: "Welcome", Message: "Welcome",
@ -289,7 +290,7 @@ func TestPushToAndroidRightTokenForStringLog(t *testing.T) {
android_token := os.Getenv("ANDROID_TEST_TOKEN") android_token := os.Getenv("ANDROID_TEST_TOKEN")
req := RequestPushNotification{ req := PushNotification{
Tokens: []string{android_token, "bbbbb"}, Tokens: []string{android_token, "bbbbb"},
Platform: 2, Platform: 2,
Message: "Welcome", Message: "Welcome",
@ -307,7 +308,7 @@ func TestOverwriteAndroidApiKey(t *testing.T) {
android_token := os.Getenv("ANDROID_TEST_TOKEN") android_token := os.Getenv("ANDROID_TEST_TOKEN")
req := RequestPushNotification{ req := PushNotification{
Tokens: []string{android_token, "bbbbb"}, Tokens: []string{android_token, "bbbbb"},
Platform: 2, Platform: 2,
Message: "Welcome", Message: "Welcome",
@ -318,3 +319,133 @@ func TestOverwriteAndroidApiKey(t *testing.T) {
success := PushToAndroid(req) success := PushToAndroid(req)
assert.False(t, success) assert.False(t, success)
} }
func TestSenMultipleNotifications(t *testing.T) {
PushConf = BuildDefaultPushConf()
PushConf.Ios.Enabled = true
PushConf.Ios.PemKeyPath = "../certificate/certificate-valid.pem"
InitAPNSClient()
PushConf.Android.Enabled = true
PushConf.Android.ApiKey = os.Getenv("ANDROID_API_KEY")
android_token := os.Getenv("ANDROID_TEST_TOKEN")
req := RequestPush{
Notifications: []PushNotification{
//ios
PushNotification{
Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"},
Platform: 1,
Message: "Welcome",
},
// android
PushNotification{
Tokens: []string{android_token, "bbbbb"},
Platform: 2,
Message: "Welcome",
},
},
}
count := SendNotification(req)
assert.Equal(t, 2, count)
}
func TestDisabledAndroidNotifications(t *testing.T) {
PushConf = BuildDefaultPushConf()
PushConf.Ios.Enabled = true
PushConf.Ios.PemKeyPath = "../certificate/certificate-valid.pem"
InitAPNSClient()
PushConf.Android.Enabled = false
PushConf.Android.ApiKey = os.Getenv("ANDROID_API_KEY")
android_token := os.Getenv("ANDROID_TEST_TOKEN")
req := RequestPush{
Notifications: []PushNotification{
//ios
PushNotification{
Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"},
Platform: 1,
Message: "Welcome",
},
// android
PushNotification{
Tokens: []string{android_token, "bbbbb"},
Platform: 2,
Message: "Welcome",
},
},
}
count := SendNotification(req)
assert.Equal(t, 1, count)
}
func TestDisabledIosNotifications(t *testing.T) {
PushConf = BuildDefaultPushConf()
PushConf.Ios.Enabled = false
PushConf.Ios.PemKeyPath = "../certificate/certificate-valid.pem"
InitAPNSClient()
PushConf.Android.Enabled = true
PushConf.Android.ApiKey = os.Getenv("ANDROID_API_KEY")
android_token := os.Getenv("ANDROID_TEST_TOKEN")
req := RequestPush{
Notifications: []PushNotification{
//ios
PushNotification{
Tokens: []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"},
Platform: 1,
Message: "Welcome",
},
// android
PushNotification{
Tokens: []string{android_token, "bbbbb"},
Platform: 2,
Message: "Welcome",
},
},
}
count := SendNotification(req)
assert.Equal(t, 1, count)
}
func TestMissingIosCertificate(t *testing.T) {
PushConf = BuildDefaultPushConf()
PushConf.Ios.Enabled = true
PushConf.Ios.PemKeyPath = "test"
err := InitAPNSClient()
assert.Error(t, err)
}
func TestAPNSClientDevHost(t *testing.T) {
PushConf = BuildDefaultPushConf()
PushConf.Ios.Enabled = true
PushConf.Ios.PemKeyPath = "../certificate/certificate-valid.pem"
InitAPNSClient()
assert.Equal(t, apns2.HostDevelopment, ApnsClient.Host)
}
func TestAPNSClientProdHost(t *testing.T) {
PushConf = BuildDefaultPushConf()
PushConf.Ios.Enabled = true
PushConf.Ios.Production = true
PushConf.Ios.PemKeyPath = "../certificate/certificate-valid.pem"
InitAPNSClient()
assert.Equal(t, apns2.HostProduction, ApnsClient.Host)
}

View File

@ -21,15 +21,20 @@ func rootHandler(c *gin.Context) {
} }
func pushHandler(c *gin.Context) { func pushHandler(c *gin.Context) {
var form RequestPushNotification var form RequestPush
if err := c.BindJSON(&form); err != nil { if err := c.BindJSON(&form); err != nil {
AbortWithError(c, http.StatusBadRequest, "Bad input request, please refer to README guide.") AbortWithError(c, http.StatusBadRequest, "Missing nitifications field.")
return
}
if len(form.Notifications) == 0 {
AbortWithError(c, http.StatusBadRequest, "Notification field is empty.")
return return
} }
// process notification. // process notification.
pushNotification(form) go SendNotification(form)
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"text": "Welcome to notification server.", "text": "Welcome to notification server.",

View File

@ -92,15 +92,28 @@ func TestAPIStatusHandler(t *testing.T) {
}) })
} }
func TestMissingParameterPushHandler(t *testing.T) { func TestMissingNotificationsParameter(t *testing.T) {
initTest() initTest()
r := gofight.New() r := gofight.New()
// missing some parameter. // missing notifications parameter.
r.POST("/api/push").
Run(GetMainEngine(), func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
assert.Equal(t, http.StatusBadRequest, r.Code)
})
}
func TestEmptyNotifications(t *testing.T) {
initTest()
r := gofight.New()
// notifications is empty.
r.POST("/api/push"). r.POST("/api/push").
SetJSON(gofight.D{ SetJSON(gofight.D{
"platform": 1, "notifications": []PushNotification{},
}). }).
Run(GetMainEngine(), func(r gofight.HTTPResponse, rq gofight.HTTPRequest) { Run(GetMainEngine(), func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
@ -108,99 +121,7 @@ func TestMissingParameterPushHandler(t *testing.T) {
}) })
} }
func TestDisabledIosPushHandler(t *testing.T) { func TestSuccessPushHandler(t *testing.T) {
initTest()
PushConf.Ios.Enabled = false
InitAPNSClient()
r := gofight.New()
r.POST("/api/push").
SetJSON(gofight.D{
"tokens": []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"},
"platform": 1,
"message": "Welcome",
}).
Run(GetMainEngine(), func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
assert.Equal(t, http.StatusOK, r.Code)
})
}
func TestMissingIosCertificate(t *testing.T) {
initTest()
PushConf.Ios.Enabled = true
PushConf.Ios.PemKeyPath = "test"
err := InitAPNSClient()
assert.Error(t, err)
}
func TestIosPushDevelopment(t *testing.T) {
initTest()
PushConf.Ios.Enabled = true
PushConf.Ios.PemKeyPath = "../certificate/certificate-valid.pem"
InitAPNSClient()
r := gofight.New()
r.POST("/api/push").
SetJSON(gofight.D{
"tokens": []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"},
"platform": 1,
"message": "Welcome",
}).
Run(GetMainEngine(), func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
assert.Equal(t, http.StatusOK, r.Code)
})
}
func TestIosPushProduction(t *testing.T) {
initTest()
PushConf.Ios.Enabled = true
PushConf.Ios.Production = true
PushConf.Ios.PemKeyPath = "../certificate/certificate-valid.pem"
InitAPNSClient()
r := gofight.New()
r.POST("/api/push").
SetJSON(gofight.D{
"tokens": []string{"11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"},
"platform": 1,
"message": "Welcome",
}).
Run(GetMainEngine(), func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
assert.Equal(t, http.StatusOK, r.Code)
})
}
func TestDisabledAndroidPushHandler(t *testing.T) {
initTest()
PushConf.Android.Enabled = false
r := gofight.New()
r.POST("/api/push").
SetJSON(gofight.D{
"tokens": []string{"aaaaaa", "bbbbb"},
"platform": 2,
"message": "Welcome",
}).
Run(GetMainEngine(), func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {
assert.Equal(t, http.StatusOK, r.Code)
})
}
func TestHalfSuccessAndroidPushHandler(t *testing.T) {
initTest() initTest()
PushConf.Android.Enabled = true PushConf.Android.Enabled = true
@ -212,31 +133,13 @@ func TestHalfSuccessAndroidPushHandler(t *testing.T) {
r.POST("/api/push"). r.POST("/api/push").
SetJSON(gofight.D{ SetJSON(gofight.D{
"tokens": []string{android_token, "bbbbb"}, "notifications": []gofight.D{
"platform": 2, gofight.D{
"message": "Welcome", "tokens": []string{android_token, "bbbbb"},
}). "platform": 2,
Run(GetMainEngine(), func(r gofight.HTTPResponse, rq gofight.HTTPRequest) { "message": "Welcome",
},
assert.Equal(t, http.StatusOK, r.Code) },
})
}
func TestAllSuccessAndroidPushHandler(t *testing.T) {
initTest()
PushConf.Android.Enabled = true
PushConf.Android.ApiKey = os.Getenv("ANDROID_API_KEY")
android_token := os.Getenv("ANDROID_TEST_TOKEN")
r := gofight.New()
r.POST("/api/push").
SetJSON(gofight.D{
"tokens": []string{android_token, android_token},
"platform": 2,
"message": "Welcome",
}). }).
Run(GetMainEngine(), func(r gofight.HTTPResponse, rq gofight.HTTPRequest) { Run(GetMainEngine(), func(r gofight.HTTPResponse, rq gofight.HTTPRequest) {