Merge pull request #390 from status-im/issue/refactor-api-notify-send-messages-#342

Refactor and little bit clean up Notify api:

Created interface and package "notification" and extracted related code into it
Set dependencies into constructor notificatotion.Manager
Mocks for notificatotion.Manager and FCMClient
Bacis unit tests for Notify and Send
Notify func is now deprecated
Notify users uses new API: message, Payload and a list of tokens
This commit is contained in:
Ivan Tomilov 2017-10-26 20:54:12 +03:00 committed by GitHub
commit 2897f0ec0f
13 changed files with 406 additions and 76 deletions

View File

@ -87,6 +87,8 @@ mock-install: ##@other Install mocking tools
mock: ##@other Regenerate mocks
mockgen -source=geth/common/types.go -destination=geth/common/types_mock.go -package=common
mockgen -source=geth/common/notification.go -destination=geth/common/notification_mock.go -package=common -imports fcm=github.com/NaySoftware/go-fcm
mockgen -source=geth/notification/fcm/client.go -destination=geth/notification/fcm/client_mock.go -package=fcm -imports fcm=github.com/NaySoftware/go-fcm
test: test-unit-coverage ##@tests Run basic, short tests during development

View File

@ -6,12 +6,12 @@ import (
"fmt"
"os"
"gopkg.in/go-playground/validator.v9"
"github.com/NaySoftware/go-fcm"
"github.com/status-im/status-go/geth/common"
"github.com/status-im/status-go/geth/log"
"github.com/status-im/status-go/geth/params"
"github.com/status-im/status-go/helpers/profiling"
"gopkg.in/go-playground/validator.v9"
)
//GenerateConfig for status node
@ -22,7 +22,7 @@ func GenerateConfig(datadir *C.char, networkID C.int, devMode C.int) *C.char {
return makeJSONResponse(err)
}
outBytes, err := json.Marshal(&config)
outBytes, err := json.Marshal(config)
if err != nil {
return makeJSONResponse(err)
}
@ -124,7 +124,7 @@ func CreateAccount(password *C.char) *C.char {
Mnemonic: mnemonic,
Error: errString,
}
outBytes, _ := json.Marshal(&out)
outBytes, _ := json.Marshal(out)
return C.CString(string(outBytes))
}
@ -144,7 +144,7 @@ func CreateChildAccount(parentAddress, password *C.char) *C.char {
PubKey: pubKey,
Error: errString,
}
outBytes, _ := json.Marshal(&out)
outBytes, _ := json.Marshal(out)
return C.CString(string(outBytes))
}
@ -165,7 +165,7 @@ func RecoverAccount(password, mnemonic *C.char) *C.char {
Mnemonic: C.GoString(mnemonic),
Error: errString,
}
outBytes, _ := json.Marshal(&out)
outBytes, _ := json.Marshal(out)
return C.CString(string(outBytes))
}
@ -207,7 +207,7 @@ func CompleteTransaction(id, password *C.char) *C.char {
Hash: txHash.Hex(),
Error: errString,
}
outBytes, err := json.Marshal(&out)
outBytes, err := json.Marshal(out)
if err != nil {
log.Error("failed to marshal CompleteTransaction output", "error", err.Error())
return makeJSONResponse(err)
@ -246,7 +246,7 @@ func CompleteTransactions(ids, password *C.char) *C.char {
}
}
outBytes, err := json.Marshal(&out)
outBytes, err := json.Marshal(out)
if err != nil {
log.Error("failed to marshal CompleteTransactions output", "error", err.Error())
return makeJSONResponse(err)
@ -270,7 +270,7 @@ func DiscardTransaction(id *C.char) *C.char {
ID: C.GoString(id),
Error: errString,
}
outBytes, err := json.Marshal(&out)
outBytes, err := json.Marshal(out)
if err != nil {
log.Error("failed to marshal DiscardTransaction output", "error", err.Error())
return makeJSONResponse(err)
@ -308,7 +308,7 @@ func DiscardTransactions(ids *C.char) *C.char {
}
}
outBytes, err := json.Marshal(&out)
outBytes, err := json.Marshal(out)
if err != nil {
log.Error("failed to marshal DiscardTransactions output", "error", err.Error())
return makeJSONResponse(err)
@ -368,14 +368,62 @@ func makeJSONResponse(err error) *C.char {
out := common.APIResponse{
Error: errString,
}
outBytes, _ := json.Marshal(&out)
outBytes, _ := json.Marshal(out)
return C.CString(string(outBytes))
}
// Notify sends push notification by given token
// @deprecated
//export Notify
func Notify(token *C.char) *C.char {
res := statusAPI.Notify(C.GoString(token))
return C.CString(res)
}
// NotifyUsers sends push notifications by given tokens.
//export NotifyUsers
func NotifyUsers(message, payloadJSON, tokensArray *C.char) (outCBytes *C.char) {
var (
err error
outBytes []byte
)
errString := ""
defer func() {
out := common.NotifyResult{
Status: err == nil,
Error: errString,
}
outBytes, err = json.Marshal(out)
if err != nil {
log.Error("failed to marshal Notify output", "error", err.Error())
outCBytes = makeJSONResponse(err)
return
}
outCBytes = C.CString(string(outBytes))
}()
tokens, err := common.ParseJSONArray(C.GoString(tokensArray))
if err != nil {
errString = err.Error()
return
}
var payload fcm.NotificationPayload
err = json.Unmarshal([]byte(C.GoString(payloadJSON)), &payload)
if err != nil {
errString = err.Error()
return
}
err = statusAPI.NotifyUsers(C.GoString(message), payload, tokens...)
if err != nil {
errString = err.Error()
return
}
return
}

View File

@ -11,10 +11,6 @@ import (
"github.com/status-im/status-go/geth/params"
)
const (
serverKey = "AAAAxwa-r08:APA91bFtMIToDVKGAmVCm76iEXtA4dn9MPvLdYKIZqAlNpLJbd12EgdBI9DSDSXKdqvIAgLodepmRhGVaWvhxnXJzVpE6MoIRuKedDV3kfHSVBhWFqsyoLTwXY4xeufL9Sdzb581U-lx"
)
// StatusAPI provides API to access Status related functionality.
type StatusAPI struct {
b *StatusBackend
@ -200,32 +196,29 @@ func (api *StatusAPI) JailBaseJS(js string) {
}
// Notify sends a push notification to the device with the given token.
// TODO(oskarth): API package this stuff
// @deprecated
func (api *StatusAPI) Notify(token string) string {
log.Debug("Notify", "token", token)
message := "Hello World1"
var NP fcm.NotificationPayload
NP.Title = "Status - new message"
NP.Body = "ping"
tokens := []string{token}
// TODO(oskarth): Experiment with this
data := map[string]string{
"msg": "Hello World1",
"sum": "Happy Day",
}
ids := []string{
token,
}
c := fcm.NewFcmClient(serverKey)
c.NewFcmRegIdsMsg(ids, data)
c.SetNotificationPayload(&NP)
_, err := c.Send()
err := api.b.newNotification().Send(message, fcm.NotificationPayload{}, tokens...)
if err != nil {
log.Error("Notify failed:", err)
}
return token
}
// NotifyUsers send notifications to users.
func (api *StatusAPI) NotifyUsers(message string, payload fcm.NotificationPayload, tokens ...string) error {
log.Debug("Notify", "tokens", tokens)
err := api.b.newNotification().Send(message, payload, tokens...)
if err != nil {
log.Error("Notify failed:", err)
}
return err
}

View File

@ -10,11 +10,17 @@ import (
"github.com/status-im/status-go/geth/jail"
"github.com/status-im/status-go/geth/log"
"github.com/status-im/status-go/geth/node"
"github.com/status-im/status-go/geth/notification/fcm"
"github.com/status-im/status-go/geth/params"
"github.com/status-im/status-go/geth/signal"
"github.com/status-im/status-go/geth/txqueue"
)
const (
//todo(jeka): should be removed
fcmServerKey = "AAAAxwa-r08:APA91bFtMIToDVKGAmVCm76iEXtA4dn9MPvLdYKIZqAlNpLJbd12EgdBI9DSDSXKdqvIAgLodepmRhGVaWvhxnXJzVpE6MoIRuKedDV3kfHSVBhWFqsyoLTwXY4xeufL9Sdzb581U-lx"
)
// StatusBackend implements Status.im service
type StatusBackend struct {
sync.Mutex
@ -23,7 +29,7 @@ type StatusBackend struct {
accountManager common.AccountManager
txQueueManager common.TxQueueManager
jailManager common.JailManager
// TODO(oskarth): notifer here
newNotification common.NotificationConstructor
}
// NewStatusBackend create a new NewStatusBackend instance
@ -34,12 +40,14 @@ func NewStatusBackend() *StatusBackend {
accountManager := account.NewManager(nodeManager)
txQueueManager := txqueue.NewManager(nodeManager, accountManager)
jailManager := jail.New(nodeManager)
notificationManager := fcm.NewNotification(fcmServerKey)
return &StatusBackend{
nodeManager: nodeManager,
accountManager: accountManager,
jailManager: jailManager,
txQueueManager: txQueueManager,
newNotification: notificationManager,
}
}

View File

@ -0,0 +1,11 @@
package common
import "github.com/NaySoftware/go-fcm"
// Notifier manages Push Notifications.
type Notifier interface {
Send(body string, payload fcm.NotificationPayload, tokens ...string) error
}
// NotificationConstructor returns constructor of configured instance Notifier interface.
type NotificationConstructor func() Notifier

View File

@ -0,0 +1,51 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: geth/common/notification.go
// Package common is a generated GoMock package.
package common
import (
go_fcm "github.com/NaySoftware/go-fcm"
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockNotifier is a mock of Notifier interface
type MockNotifier struct {
ctrl *gomock.Controller
recorder *MockNotifierMockRecorder
}
// MockNotifierMockRecorder is the mock recorder for MockNotifier
type MockNotifierMockRecorder struct {
mock *MockNotifier
}
// NewMockNotifier creates a new mock instance
func NewMockNotifier(ctrl *gomock.Controller) *MockNotifier {
mock := &MockNotifier{ctrl: ctrl}
mock.recorder = &MockNotifierMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockNotifier) EXPECT() *MockNotifierMockRecorder {
return m.recorder
}
// Send mocks base method
func (m *MockNotifier) Send(body string, payload go_fcm.NotificationPayload, tokens ...string) error {
varargs := []interface{}{body, payload}
for _, a := range tokens {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Send", varargs...)
ret0, _ := ret[0].(error)
return ret0
}
// Send indicates an expected call of Send
func (mr *MockNotifierMockRecorder) Send(body, payload interface{}, tokens ...interface{}) *gomock.Call {
varargs := append([]interface{}{body, payload}, tokens...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockNotifier)(nil).Send), varargs...)
}

View File

@ -395,6 +395,12 @@ type TestConfig struct {
}
}
// NotifyResult is a JSON returned from notify message
type NotifyResult struct {
Status bool `json:"status"`
Error string `json:"error,omitempty"`
}
// LoadTestConfig loads test configuration values from disk
func LoadTestConfig() (*TestConfig, error) {
var testConfig TestConfig

View File

@ -0,0 +1,10 @@
package fcm
import "github.com/NaySoftware/go-fcm"
// firebaseClient is a copy of "go-fcm" client methods.
type firebaseClient interface {
NewFcmRegIdsMsg(tokens []string, body interface{}) *fcm.FcmClient
Send() (*fcm.FcmResponseStatus, error)
SetNotificationPayload(payload *fcm.NotificationPayload) *fcm.FcmClient
}

View File

@ -0,0 +1,71 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: geth/notification/fcm/client.go
// Package fcm is a generated GoMock package.
package fcm
import (
go_fcm "github.com/NaySoftware/go-fcm"
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockfirebaseClient is a mock of firebaseClient interface
type MockfirebaseClient struct {
ctrl *gomock.Controller
recorder *MockfirebaseClientMockRecorder
}
// MockfirebaseClientMockRecorder is the mock recorder for MockfirebaseClient
type MockfirebaseClientMockRecorder struct {
mock *MockfirebaseClient
}
// NewMockfirebaseClient creates a new mock instance
func NewMockfirebaseClient(ctrl *gomock.Controller) *MockfirebaseClient {
mock := &MockfirebaseClient{ctrl: ctrl}
mock.recorder = &MockfirebaseClientMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockfirebaseClient) EXPECT() *MockfirebaseClientMockRecorder {
return m.recorder
}
// NewFcmRegIdsMsg mocks base method
func (m *MockfirebaseClient) NewFcmRegIdsMsg(tokens []string, body interface{}) *go_fcm.FcmClient {
ret := m.ctrl.Call(m, "NewFcmRegIdsMsg", tokens, body)
ret0, _ := ret[0].(*go_fcm.FcmClient)
return ret0
}
// NewFcmRegIdsMsg indicates an expected call of NewFcmRegIdsMsg
func (mr *MockfirebaseClientMockRecorder) NewFcmRegIdsMsg(tokens, body interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewFcmRegIdsMsg", reflect.TypeOf((*MockfirebaseClient)(nil).NewFcmRegIdsMsg), tokens, body)
}
// Send mocks base method
func (m *MockfirebaseClient) Send() (*go_fcm.FcmResponseStatus, error) {
ret := m.ctrl.Call(m, "Send")
ret0, _ := ret[0].(*go_fcm.FcmResponseStatus)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Send indicates an expected call of Send
func (mr *MockfirebaseClientMockRecorder) Send() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockfirebaseClient)(nil).Send))
}
// SetNotificationPayload mocks base method
func (m *MockfirebaseClient) SetNotificationPayload(payload *go_fcm.NotificationPayload) *go_fcm.FcmClient {
ret := m.ctrl.Call(m, "SetNotificationPayload", payload)
ret0, _ := ret[0].(*go_fcm.FcmClient)
return ret0
}
// SetNotificationPayload indicates an expected call of SetNotificationPayload
func (mr *MockfirebaseClientMockRecorder) SetNotificationPayload(payload interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetNotificationPayload", reflect.TypeOf((*MockfirebaseClient)(nil).SetNotificationPayload), payload)
}

View File

@ -0,0 +1,47 @@
package fcm
import (
"fmt"
"github.com/NaySoftware/go-fcm"
"github.com/status-im/status-go/geth/common"
)
// Notification represents messaging provider for notifications.
type Notification struct {
client firebaseClient
}
// NewNotification Firebase Cloud Messaging client constructor.
func NewNotification(key string) common.NotificationConstructor {
return func() common.Notifier {
client := fcm.NewFcmClient(key).
SetDelayWhileIdle(true).
SetContentAvailable(true).
SetTimeToLive(fcm.MAX_TTL)
return &Notification{client}
}
}
// Send send to the tokens list.
func (n *Notification) Send(body string, payload fcm.NotificationPayload, tokens ...string) error {
data := map[string]string{
"msg": body,
}
if payload.Title == "" {
payload.Title = "Status - new message"
}
if payload.Body == "" {
payload.Body = "ping"
}
fmt.Println(payload.Title, payload.Body)
n.client.NewFcmRegIdsMsg(tokens, data)
n.client.SetNotificationPayload(&payload)
_, err := n.client.Send()
return err
}

View File

@ -0,0 +1,71 @@
package fcm
import (
"errors"
"testing"
"github.com/NaySoftware/go-fcm"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/suite"
)
func TestFCMClientTestSuite(t *testing.T) {
suite.Run(t, new(NotifierTestSuite))
}
type NotifierTestSuite struct {
suite.Suite
fcmClientMock *MockfirebaseClient
fcmClientMockCtrl *gomock.Controller
}
func (s *NotifierTestSuite) SetupTest() {
s.fcmClientMockCtrl = gomock.NewController(s.T())
s.fcmClientMock = NewMockfirebaseClient(s.fcmClientMockCtrl)
}
func (s *NotifierTestSuite) TearDownTest() {
s.fcmClientMockCtrl.Finish()
}
func (s *NotifierTestSuite) TestNotifySuccess() {
fcmPayload := getPayload()
ids := []string{"1"}
payload := fcmPayload
msg := make(map[string]string)
body := "body"
msg["msg"] = body
s.fcmClientMock.EXPECT().SetNotificationPayload(&fcmPayload).Times(1)
s.fcmClientMock.EXPECT().NewFcmRegIdsMsg(ids, msg).Times(1)
s.fcmClientMock.EXPECT().Send().Return(nil, nil).Times(1)
fcmClient := Notification{s.fcmClientMock}
err := fcmClient.Send(body, payload, ids...)
s.NoError(err)
}
func (s *NotifierTestSuite) TestNotifyError() {
expectedError := errors.New("error")
fcmPayload := getPayload()
ids := []string{"1"}
payload := fcmPayload
msg := make(map[string]string)
body := "body"
msg["msg"] = body
s.fcmClientMock.EXPECT().SetNotificationPayload(&fcmPayload).Times(1)
s.fcmClientMock.EXPECT().NewFcmRegIdsMsg(ids, msg).Times(1)
s.fcmClientMock.EXPECT().Send().Return(nil, expectedError).Times(1)
fcmClient := Notification{s.fcmClientMock}
err := fcmClient.Send(body, payload, ids...)
s.Equal(expectedError, err)
}
func getPayload() fcm.NotificationPayload {
return fcm.NotificationPayload{Title: "Status - new message", Body: "sum"}
}

View File

@ -0,0 +1,12 @@
package notification
// Payload data of message.
type Payload struct {
Title string
Body string
Icon string
Sound string
Badge string
Tag string
Color string
}

View File

@ -578,6 +578,7 @@ type bintree struct {
Func func() (*asset, error)
Children map[string]*bintree
}
var _bintree = &bintree{nil, map[string]*bintree{
"config": &bintree{nil, map[string]*bintree{
"cht.json": &bintree{configChtJson, map[string]*bintree{}},
@ -660,4 +661,3 @@ func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}