1+ // ParsePushAdapter is the default implementation of
2+ // PushAdapter, it uses GCM for android push and APNS
3+ // for ios push.
4+ var Parse = require ( 'parse/node' ) . Parse ,
5+ GCM = require ( './GCM' ) ,
6+ APNS = require ( './APNS' ) ;
7+
8+ function ParsePushAdapter ( ) {
9+ this . validPushTypes = [ 'ios' , 'android' ] ;
10+ this . senders = { } ;
11+ }
12+
13+ ParsePushAdapter . prototype . registerPushSenders = function ( pushConfig ) {
14+ // Initialize senders
15+ for ( var i = 0 ; i < this . validPushTypes . length ; i ++ ) {
16+ this . senders [ this . validPushTypes [ i ] ] = [ ] ;
17+ }
18+
19+ pushConfig = pushConfig || { } ;
20+ var pushTypes = Object . keys ( pushConfig ) ;
21+ for ( var i = 0 ; i < pushTypes . length ; i ++ ) {
22+ var pushType = pushTypes [ i ] ;
23+ if ( this . validPushTypes . indexOf ( pushType ) < 0 ) {
24+ throw new Parse . Error ( Parse . Error . PUSH_MISCONFIGURED ,
25+ 'Push to ' + pushTypes + ' is not supported' ) ;
26+ }
27+
28+ var typePushConfig = pushConfig [ pushType ] ;
29+ var senderArgs = [ ] ;
30+ // Since for ios, there maybe multiple cert/key pairs,
31+ // typePushConfig can be an array.
32+ if ( Array . isArray ( typePushConfig ) ) {
33+ senderArgs = senderArgs . concat ( typePushConfig ) ;
34+ } else if ( typeof typePushConfig === 'object' ) {
35+ senderArgs . push ( typePushConfig ) ;
36+ } else {
37+ throw new Parse . Error ( Parse . Error . PUSH_MISCONFIGURED ,
38+ 'Push Configuration is invalid' ) ;
39+ }
40+ for ( var j = 0 ; j < senderArgs . length ; j ++ ) {
41+ var senderArg = senderArgs [ j ] ;
42+ var sender ;
43+ switch ( pushType ) {
44+ case 'ios' :
45+ sender = new APNS ( senderArg ) ;
46+ break ;
47+ case 'android' :
48+ sender = new GCM ( senderArg ) ;
49+ break ;
50+ }
51+ this . senders [ pushType ] . push ( sender ) ;
52+ }
53+ }
54+ }
55+
56+ /**
57+ * Get an array of push senders based on the push type.
58+ * @param {String } The push type
59+ * @returns {Array|Undefined } An array of push senders
60+ */
61+ ParsePushAdapter . prototype . getPushSenders = function ( pushType ) {
62+ if ( ! this . senders [ pushType ] ) {
63+ console . log ( 'No push sender for push type %s' , pushType ) ;
64+ return [ ] ;
65+ }
66+ return this . senders [ pushType ] ;
67+ }
68+
69+ /**
70+ * Get an array of valid push types.
71+ * @returns {Array } An array of valid push types
72+ */
73+ ParsePushAdapter . prototype . getValidPushTypes = function ( ) {
74+ return this . validPushTypes ;
75+ }
76+
77+ ParsePushAdapter . prototype . send = function ( data , installations ) {
78+ var deviceTokenMap = classifyDeviceTokens ( installations , this . validPushTypes ) ;
79+ var sendPromises = [ ] ;
80+ for ( var pushType in deviceTokenMap ) {
81+ var senders = this . getPushSenders ( pushType ) ;
82+ // Since ios have dev/prod cert, a push type may have multiple senders
83+ for ( var i = 0 ; i < senders . length ; i ++ ) {
84+ var sender = senders [ i ] ;
85+ var deviceTokens = deviceTokenMap [ pushType ] ;
86+ if ( ! sender || deviceTokens . length == 0 ) {
87+ continue ;
88+ }
89+ // For android, we can only have 1000 recepients per send
90+ var chunkDeviceTokens = sliceDeviceTokens ( pushType , deviceTokens , GCM . GCMRegistrationTokensMax ) ;
91+ for ( var j = 0 ; j < chunkDeviceTokens . length ; j ++ ) {
92+ sendPromises . push ( sender . send ( data , chunkDeviceTokens [ j ] ) ) ;
93+ }
94+ }
95+ }
96+ return Parse . Promise . when ( sendPromises ) ;
97+ }
98+
99+ /**
100+ * Classify the device token of installations based on its device type.
101+ * @param {Object } installations An array of installations
102+ * @param {Array } validPushTypes An array of valid push types(string)
103+ * @returns {Object } A map whose key is device type and value is an array of device tokens
104+ */
105+ function classifyDeviceTokens ( installations , validPushTypes ) {
106+ // Init deviceTokenMap, create a empty array for each valid pushType
107+ var deviceTokenMap = { } ;
108+ for ( var i = 0 ; i < validPushTypes . length ; i ++ ) {
109+ deviceTokenMap [ validPushTypes [ i ] ] = [ ] ;
110+ }
111+ for ( var i = 0 ; i < installations . length ; i ++ ) {
112+ var installation = installations [ i ] ;
113+ // No deviceToken, ignore
114+ if ( ! installation . deviceToken ) {
115+ continue ;
116+ }
117+ var pushType = installation . deviceType ;
118+ if ( deviceTokenMap [ pushType ] ) {
119+ deviceTokenMap [ pushType ] . push ( installation . deviceToken ) ;
120+ } else {
121+ console . log ( 'Unknown push type from installation %j' , installation ) ;
122+ }
123+ }
124+ return deviceTokenMap ;
125+ }
126+
127+ /**
128+ * Slice a list of device tokens to several list of device tokens with fixed chunk size.
129+ * @param {String } pushType The push type of the given device tokens
130+ * @param {Array } deviceTokens An array of device tokens(string)
131+ * @param {Number } chunkSize The size of the a chunk
132+ * @returns {Array } An array which contaisn several arries of device tokens with fixed chunk size
133+ */
134+ function sliceDeviceTokens ( pushType , deviceTokens , chunkSize ) {
135+ if ( pushType !== 'android' ) {
136+ return [ deviceTokens ] ;
137+ }
138+ var chunkDeviceTokens = [ ] ;
139+ while ( deviceTokens . length > 0 ) {
140+ chunkDeviceTokens . push ( deviceTokens . splice ( 0 , chunkSize ) ) ;
141+ }
142+ return chunkDeviceTokens ;
143+ }
144+
145+ if ( typeof process !== 'undefined' && process . env . NODE_ENV === 'test' ) {
146+ ParsePushAdapter . classifyDeviceTokens = classifyDeviceTokens ;
147+ ParsePushAdapter . sliceDeviceTokens = sliceDeviceTokens ;
148+ }
149+ module . exports = ParsePushAdapter ;
0 commit comments