code comments - godoc

This commit is contained in:
ahmad 2016-07-10 23:14:20 +03:00
parent 079508a609
commit 1b3a6b7e10
2 changed files with 101 additions and 43 deletions

102
fcm.go
View File

@ -7,35 +7,41 @@ import (
"io/ioutil"
"net/http"
"time"
// "github.com/jpillora/backoff"
)
const (
// fcm_server_url fcm server url
fcm_server_url = "https://fcm.googleapis.com/fcm/send"
// MAX_TTL the default ttl for a notification
MAX_TTL = 2419200
// Priority_HIGH notification priority
Priority_HIGH = "high"
// Priority_NORMAL notification priority
Priority_NORMAL = "normal"
// retry_after_header header name
retry_after_header = "Retry-After"
// error_key readable error caching !
error_key = "error"
)
var (
// minBackoff = 1 * time.Second
// maxBackoff = 10 * time.Second
// retreyableErrors whether the error is a retryable
retreyableErrors = map[string]bool{
"Unavailable": true,
"InternalServerError": true,
}
// for testing purposes
// fcmServerUrl for testing purposes
fcmServerUrl = fcm_server_url
)
// FcmClient stores the key and the Message (FcmMsg)
type FcmClient struct {
ApiKey string
Message FcmMsg
}
// FcmMsg represents fcm request message
type FcmMsg struct {
Data map[string]string `json:"data,omitempty"`
To string `json:"to,omitempty"`
@ -51,6 +57,7 @@ type FcmMsg struct {
Condition string `json:"condition,omitempty"`
}
// FcmMsg represents fcm response message - (tokens and topics)
type FcmResponseStatus struct {
Ok bool
StatusCode int
@ -64,6 +71,7 @@ type FcmResponseStatus struct {
RetryAfter string
}
// NotificationPayload notification message payload
type NotificationPayload struct {
Title string `json:"title,omitempty"`
Body string `json:"body,omitempty"`
@ -79,24 +87,8 @@ type NotificationPayload struct {
TitleLocArgs string `json:"title_loc_args,omitempty"`
}
type InstanceIdResult struct {
Application string `json:"application,omitempty"`
AuthorizedEntity string `json:"authorizedEntity,omitempty"`
ApplicationVersion string `json:"applicationVersion,omitempty"`
AppSigner string `json:"appSigner,omitempty"`
AttestStatus string `json:"attestStatus,omitempty"`
Platform string `json:"platform,omitempty"`
connectionType string `json:"connectionType,omitempty"`
connectDate string `json:"connectDate,omitempty"`
rel map[string]InstanceIdRelTopics `json:"rel,omitempty"`
}
type InstanceIdRelTopics struct {
TopicName map[string]TopicDate
}
type TopicDate map[string]string
// NewFcmClient init and create fcm client
func NewFcmClient(apiKey string) *FcmClient {
fcmc := new(FcmClient)
fcmc.ApiKey = apiKey
@ -104,6 +96,7 @@ func NewFcmClient(apiKey string) *FcmClient {
return fcmc
}
// NewFcmTopicMsg sets the targeted token/topic and the data payload
func (this *FcmClient) NewFcmTopicMsg(to string, body map[string]string) *FcmClient {
this.NewFcmMsgTo(to, body)
@ -111,6 +104,7 @@ func (this *FcmClient) NewFcmTopicMsg(to string, body map[string]string) *FcmCli
return this
}
// NewFcmMsgTo sets the targeted token/topic and the data payload
func (this *FcmClient) NewFcmMsgTo(to string, body map[string]string) *FcmClient {
this.Message.To = to
this.Message.Data = body
@ -118,6 +112,7 @@ func (this *FcmClient) NewFcmMsgTo(to string, body map[string]string) *FcmClient
return this
}
// SetMsgData sets data payload
func (this *FcmClient) SetMsgData(body map[string]string) *FcmClient {
this.Message.Data = body
@ -126,6 +121,7 @@ func (this *FcmClient) SetMsgData(body map[string]string) *FcmClient {
}
// NewFcmRegIdsMsg gets a list of devices with data payload
func (this *FcmClient) NewFcmRegIdsMsg(list []string, body map[string]string) *FcmClient {
this.newDevicesList(list)
this.Message.Data = body
@ -134,6 +130,7 @@ func (this *FcmClient) NewFcmRegIdsMsg(list []string, body map[string]string) *F
}
// newDevicesList init the devices list
func (this *FcmClient) newDevicesList(list []string) *FcmClient {
this.Message.RegistrationIds = make([]string, len(list))
copy(this.Message.RegistrationIds, list)
@ -142,6 +139,7 @@ func (this *FcmClient) newDevicesList(list []string) *FcmClient {
}
// AppendDevices adds more devices/tokens to the Fcm request
func (this *FcmClient) AppendDevices(list []string) *FcmClient {
this.Message.RegistrationIds = append(this.Message.RegistrationIds, list...)
@ -149,10 +147,12 @@ func (this *FcmClient) AppendDevices(list []string) *FcmClient {
return this
}
// apiKeyHeader generates the value of the Authorization key
func (this *FcmClient) apiKeyHeader() string {
return fmt.Sprintf("key=%v", this.ApiKey)
}
// sendOnce send a single request to fcm
func (this *FcmClient) sendOnce() (*FcmResponseStatus, error) {
fcmRespStatus := new(FcmResponseStatus)
@ -208,16 +208,19 @@ func (this *FcmClient) sendOnce() (*FcmResponseStatus, error) {
}
// Send to fcm
func (this *FcmClient) Send() (*FcmResponseStatus, error) {
return this.sendOnce()
}
// toJsonByte converts FcmMsg to a json byte
func (this *FcmMsg) toJsonByte() ([]byte, error) {
return json.Marshal(this)
}
// parseStatusBody parse FCM response body
func (this *FcmResponseStatus) parseStatusBody(body []byte) error {
if err := json.Unmarshal([]byte(body), &this); err != nil {
@ -227,6 +230,8 @@ func (this *FcmResponseStatus) parseStatusBody(body []byte) error {
}
// SetPriorety Sets the priority of the message.
// Priority_HIGH or Priority_NORMAL
func (this *FcmClient) SetPriorety(p string) {
if p == Priority_HIGH {
@ -236,6 +241,11 @@ func (this *FcmClient) SetPriorety(p string) {
}
}
// SetCollapseKey This parameter identifies a group of messages
// (e.g., with collapse_key: "Updates Available") that can be collapsed,
// so that only the last message gets sent when delivery can be resumed.
// This is intended to avoid sending too many of the same messages when the
// device comes back online or becomes active (see delay_while_idle).
func (this *FcmClient) SetCollapseKey(val string) *FcmClient {
this.Message.CollapseKey = val
@ -243,6 +253,8 @@ func (this *FcmClient) SetCollapseKey(val string) *FcmClient {
return this
}
// SetNotificationPayload sets the notification payload based on the specs
// https://firebase.google.com/docs/cloud-messaging/http-server-ref
func (this *FcmClient) SetNotificationPayload(payload *NotificationPayload) *FcmClient {
this.Message.Notification = *payload
@ -250,6 +262,10 @@ func (this *FcmClient) SetNotificationPayload(payload *NotificationPayload) *Fcm
return this
}
// SetContentAvailable On iOS, use this field to represent content-available
// in the APNS payload. When a notification or message is sent and this is set
// to true, an inactive client app is awoken. On Android, data messages wake
// the app by default. On Chrome, currently not supported.
func (this *FcmClient) SetContentAvailable(isContentAvailable bool) *FcmClient {
this.Message.ContentAvailable = isContentAvailable
@ -257,12 +273,21 @@ func (this *FcmClient) SetContentAvailable(isContentAvailable bool) *FcmClient {
return this
}
// SetDelayWhileIdle When this parameter is set to true, it indicates that
// the message should not be sent until the device becomes active.
// The default value is false.
func (this *FcmClient) SetDelayWhileIdle(isDelayWhileIdle bool) *FcmClient {
this.Message.DelayWhileIdle = isDelayWhileIdle
return this
}
// SetTimeToLive This parameter specifies how long (in seconds) the message
// should be kept in FCM storage if the device is offline. The maximum time
// to live supported is 4 weeks, and the default value is 4 weeks.
// For more information, see
// https://firebase.google.com/docs/cloud-messaging/concept-options#ttl
func (this *FcmClient) SetTimeToLive(ttl int) *FcmClient {
if ttl > MAX_TTL {
@ -277,6 +302,9 @@ func (this *FcmClient) SetTimeToLive(ttl int) *FcmClient {
return this
}
// SetRestrictedPackageName This parameter specifies the package name of the
// application where the registration tokens must match in order to
// receive the message.
func (this *FcmClient) SetRestrictedPackageName(pkg string) *FcmClient {
this.Message.RestrictedPackageName = pkg
@ -284,6 +312,9 @@ func (this *FcmClient) SetRestrictedPackageName(pkg string) *FcmClient {
return this
}
// SetDryRun This parameter, when set to true, allows developers to test
// a request without actually sending a message.
// The default value is false
func (this *FcmClient) SetDryRun(drun bool) *FcmClient {
this.Message.DryRun = drun
@ -291,6 +322,7 @@ func (this *FcmClient) SetDryRun(drun bool) *FcmClient {
return this
}
// PrintResults prints the FcmResponseStatus results for fast using and debugging
func (this *FcmResponseStatus) PrintResults() {
fmt.Println("Status Code :", this.StatusCode)
fmt.Println("Success :", this.Success)
@ -306,6 +338,8 @@ func (this *FcmResponseStatus) PrintResults() {
}
}
// IsTimeout check whether the response timeout based on http response status
// code and if any error is retryable
func (this *FcmResponseStatus) IsTimeout() bool {
if this.StatusCode > 500 {
return true
@ -322,33 +356,15 @@ func (this *FcmResponseStatus) IsTimeout() bool {
return false
}
// GetRetryAfterTime converts the retrey after response header
// to a time.Duration
func (this *FcmResponseStatus) GetRetryAfterTime() (t time.Duration, e error) {
t, e = time.ParseDuration(this.RetryAfter)
return
}
// SetCondition to set a logical expression of conditions that determine the message target
func (this *FcmClient) SetCondition(condition string) *FcmClient {
this.Message.Condition = condition
return this
}
// func newBackoffHandler() *backoff.Backoff {
// b := &backoff.Backoff{
//
// Min: minBackoff,
// Max: maxBackoff,
// Jitter: true,
// }
//
// return b
// }
// func setMinBackoff(m time.Duration) {
// minBackoff = m
// }
//
// func setMaxBackoff(m time.Duration) {
// maxBackoff = m
// }

View File

@ -11,23 +11,37 @@ import (
)
const (
// instance_id_info_with_details_srv_url
instance_id_info_with_details_srv_url = "https://iid.googleapis.com/iid/info/%s?details=true"
// instance_id_info_no_details_srv_url
instance_id_info_no_details_srv_url = "https://iid.googleapis.com/iid/info/%s"
// subscribe_instanceid_to_topic_srv_url
subscribe_instanceid_to_topic_srv_url = "https://iid.googleapis.com/iid/v1/%s/rel/topics/%s"
// batch_add_srv_url
batch_add_srv_url = "https://iid.googleapis.com/iid/v1:batchAdd"
// batch_rem_srv_url
batch_rem_srv_url = "https://iid.googleapis.com/iid/v1:batchRemove"
// apns_batch_import_srv_url
apns_batch_import_srv_url = "https://iid.googleapis.com/iid/v1:batchImport"
// apns_token_key
apns_token_key = "apns_token"
// status_key
status_key = "status"
// reg_token_key
reg_token_key = "registration_token"
// topics
topics = "/topics/"
)
var (
// batchErrors response errors
batchErrors = map[string]bool{
"NOT_FOUND": true,
"INVALID_ARGUMENT": true,
@ -36,6 +50,7 @@ var (
}
)
// InstanceIdInfoResponse response for instance id info request
type InstanceIdInfoResponse struct {
Application string `json:"application,omitempty"`
AuthorizedEntity string `json:"authorizedEntity,omitempty"`
@ -49,17 +64,21 @@ type InstanceIdInfoResponse struct {
Rel map[string]map[string]map[string]string `json:"rel,omitempty"`
}
// SubscribeResponse response for single topic subscribtion
type SubscribeResponse struct {
Error string `json:"error,omitempty"`
Status string
StatusCode int
}
// BatchRequest add/remove request
type BatchRequest struct {
To string `json:"to,omitempty"`
RegTokens []string `json:"registration_tokens,omitempty"`
}
// BatchResponse add/remove response
type BatchResponse struct {
Error string `json:"error,omitempty"`
Results []map[string]string `json:"results,omitempty"`
@ -67,12 +86,16 @@ type BatchResponse struct {
StatusCode int
}
// ApnsBatchRequest apns import request
type ApnsBatchRequest struct {
App string `json:"application,omitempty"`
Sandbox bool `json:"sandbox,omitempty"`
ApnsTokens []string `json:"apns_tokens,omitempty"`
}
// ApnsBatchResponse apns import response
type ApnsBatchResponse struct {
Results []map[string]string `json:"results,omitempty"`
Error string `json:"error,omitempty"`
@ -80,6 +103,8 @@ type ApnsBatchResponse struct {
StatusCode int
}
// GetInfo gets the instance id info
func (this *FcmClient) GetInfo(withDetails bool, instanceIdToken string) (*InstanceIdInfoResponse, error) {
var request_url string = generateGetInfoUrl(instance_id_info_no_details_srv_url, instanceIdToken)
@ -117,6 +142,7 @@ func (this *FcmClient) GetInfo(withDetails bool, instanceIdToken string) (*Insta
return infoResponse, nil
}
// parseGetInfo parses response to InstanceIdInfoResponse
func parseGetInfo(body []byte) (*InstanceIdInfoResponse, error) {
info := new(InstanceIdInfoResponse)
@ -129,6 +155,7 @@ func parseGetInfo(body []byte) (*InstanceIdInfoResponse, error) {
}
// PrintResults prints InstanceIdInfoResponse, for faster debugging
func (this *InstanceIdInfoResponse) PrintResults() {
fmt.Println("Error : ", this.Error)
fmt.Println("App : ", this.Application)
@ -149,10 +176,12 @@ func (this *InstanceIdInfoResponse) PrintResults() {
}
}
// generateGetInfoUrl generate based on with details and the instance token
func generateGetInfoUrl(srv string, instanceIdToken string) string {
return fmt.Sprintf(srv, instanceIdToken)
}
// SubscribeToTopic subscribes a single device/token to a topic
func (this *FcmClient) SubscribeToTopic(instanceIdToken string, topic string) (*SubscribeResponse, error) {
request, err := http.NewRequest("POST", generateSubToTopicUrl(instanceIdToken, topic), nil)
@ -184,6 +213,7 @@ func (this *FcmClient) SubscribeToTopic(instanceIdToken string, topic string) (*
return subResponse, nil
}
// parseSubscribeResponse converts a byte response to a SubscribeResponse
func parseSubscribeResponse(body []byte, resp *http.Response) (*SubscribeResponse, error) {
subResp := new(SubscribeResponse)
@ -197,6 +227,7 @@ func parseSubscribeResponse(body []byte, resp *http.Response) (*SubscribeRespons
return subResp, nil
}
// PrintResults prints SubscribeResponse, for faster debugging
func (this *SubscribeResponse) PrintResults() {
fmt.Println("Response Status: ", this.Status)
@ -207,6 +238,7 @@ func (this *SubscribeResponse) PrintResults() {
}
// generateSubToTopicUrl generates a url based on the instnace id and topic name
func generateSubToTopicUrl(instaceId string, topic string) string {
Tmptopic := strings.ToLower(topic)
if strings.Contains(Tmptopic, "/topics/") {
@ -216,6 +248,7 @@ func generateSubToTopicUrl(instaceId string, topic string) string {
return fmt.Sprintf(subscribe_instanceid_to_topic_srv_url, instaceId, topic)
}
// BatchSubscribeToTopic subscribes (many) devices/tokens to a given topic
func (this *FcmClient) BatchSubscribeToTopic(tokens []string, topic string) (*BatchResponse, error) {
jsonByte, err := generateBatchRequest(tokens, topic)
@ -256,6 +289,7 @@ func (this *FcmClient) BatchSubscribeToTopic(tokens []string, topic string) (*Ba
return result, nil
}
// BatchUnsubscribeFromTopic unsubscribes (many) devices/tokens from a given topic
func (this *FcmClient) BatchUnsubscribeFromTopic(tokens []string, topic string) (*BatchResponse, error) {
jsonByte, err := generateBatchRequest(tokens, topic)
@ -297,6 +331,7 @@ func (this *FcmClient) BatchUnsubscribeFromTopic(tokens []string, topic string)
return result, nil
}
// PrintResults prints BatchResponse, for faster debugging
func (this *BatchResponse) PrintResults() {
fmt.Println("Error : ", this.Error)
fmt.Println("Status : ", this.Status)
@ -308,6 +343,7 @@ func (this *BatchResponse) PrintResults() {
}
}
// generateBatchRequest based on tokens and topic
func generateBatchRequest(tokens []string, topic string) ([]byte, error) {
envelope := new(BatchRequest)
envelope.To = topics + extractTopicName(topic)
@ -318,6 +354,7 @@ func generateBatchRequest(tokens []string, topic string) ([]byte, error) {
}
// extractTopicName extract topic name for valid topic name input
func extractTopicName(inTopic string) (result string) {
Tmptopic := strings.ToLower(inTopic)
if strings.Contains(Tmptopic, "/topics/") {
@ -330,6 +367,7 @@ func extractTopicName(inTopic string) (result string) {
return
}
// generateBatchResponse converts a byte response to BatchResponse
func generateBatchResponse(resp []byte) (*BatchResponse, error) {
result := new(BatchResponse)
@ -341,6 +379,7 @@ func generateBatchResponse(resp []byte) (*BatchResponse, error) {
}
// ApnsBatchImportRequest apns import requst
func (this *FcmClient) ApnsBatchImportRequest(apnsReq *ApnsBatchRequest) (*ApnsBatchResponse, error) {
jsonByte, err := apnsReq.ToByte()
@ -384,6 +423,7 @@ func (this *FcmClient) ApnsBatchImportRequest(apnsReq *ApnsBatchRequest) (*ApnsB
return result, nil
}
// ToByte converts ApnsBatchRequest to a byte
func (this *ApnsBatchRequest) ToByte() ([]byte, error) {
data, err := json.Marshal(this)
if err != nil {
@ -393,6 +433,7 @@ func (this *ApnsBatchRequest) ToByte() ([]byte, error) {
return data, nil
}
// parseApnsBatchResponse converts apns byte response to ApnsBatchResponse
func parseApnsBatchResponse(resp []byte) (*ApnsBatchResponse, error) {
result := new(ApnsBatchResponse)
@ -404,6 +445,7 @@ func parseApnsBatchResponse(resp []byte) (*ApnsBatchResponse, error) {
}
// PrintResults prints ApnsBatchResponse, for faster debugging
func (this *ApnsBatchResponse) PrintResults() {
fmt.Println("Status : ", this.Status)
fmt.Println("StatusCode : ", this.StatusCode)