From 1b3a6b7e10645206ce659613a6fac5c370c258e6 Mon Sep 17 00:00:00 2001 From: ahmad Date: Sun, 10 Jul 2016 23:14:20 +0300 Subject: [PATCH] code comments - godoc --- fcm.go | 102 +++++++++++++++++++++++++++++--------------------- instanceid.go | 42 +++++++++++++++++++++ 2 files changed, 101 insertions(+), 43 deletions(-) diff --git a/fcm.go b/fcm.go index cb26c26..1379391 100644 --- a/fcm.go +++ b/fcm.go @@ -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 -// } diff --git a/instanceid.go b/instanceid.go index 7b79bb2..cfcead9 100644 --- a/instanceid.go +++ b/instanceid.go @@ -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)