jpush 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.project +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +339 -0
- data/README.md +75 -0
- data/Rakefile +1 -0
- data/example/push_example.rb +101 -0
- data/example/report_example.rb +18 -0
- data/jpush.gemspec +24 -0
- data/lib/jpush.rb +20 -0
- data/lib/jpush/http_client.rb +124 -0
- data/lib/jpush/jpush_client.rb +75 -0
- data/lib/jpush/model/audience.rb +67 -0
- data/lib/jpush/model/message.rb +37 -0
- data/lib/jpush/model/messages_result.rb +7 -0
- data/lib/jpush/model/notification/android_notification.rb +38 -0
- data/lib/jpush/model/notification/ios_notification.rb +60 -0
- data/lib/jpush/model/notification/notification.rb +35 -0
- data/lib/jpush/model/notification/winphone_notification.rb +37 -0
- data/lib/jpush/model/options.rb +53 -0
- data/lib/jpush/model/platform.rb +57 -0
- data/lib/jpush/model/push_payload.rb +70 -0
- data/lib/jpush/model/push_result.rb +32 -0
- data/lib/jpush/model/receiveds_result.rb +62 -0
- data/lib/jpush/push_client.rb +31 -0
- data/lib/jpush/report_client.rb +45 -0
- data/lib/jpush/response_wrapper.rb +36 -0
- data/lib/jpush/util/service_helper.rb +10 -0
- data/test/alert_override_tests.rb +62 -0
- data/test/audience_tests.rb +169 -0
- data/test/base_remote_tests.rb +27 -0
- data/test/message_tests.rb +49 -0
- data/test/notification_tests.rb +52 -0
- data/test/push_payload_test.rb +58 -0
- data/test/report_function_tests.rb +40 -0
- metadata +101 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
path= File.expand_path('../', __FILE__)
|
2
|
+
require File.join(path, 'http_client.rb')
|
3
|
+
require File.join(path, 'model/push_result.rb')
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
=begin
|
7
|
+
Entrance for sending Push.
|
8
|
+
timeToLive If not present, the default is 86400(s) (one day).
|
9
|
+
=end
|
10
|
+
module JPush
|
11
|
+
class PushClient
|
12
|
+
@@PUSH_API_URL = 'https://api.jpush.cn/v3/push'
|
13
|
+
@@_timeToLive = 60 * 60 * 24
|
14
|
+
def initialize(maxRetryTimes)
|
15
|
+
@httpclient = JPush::NativeHttpClient.new(maxRetryTimes)
|
16
|
+
end
|
17
|
+
|
18
|
+
=begin
|
19
|
+
@param payload is the instance of PushPayload
|
20
|
+
@autoCode
|
21
|
+
=end
|
22
|
+
def sendPush(payload,autoCode)
|
23
|
+
json_data = JSON.generate(payload.toJSON)
|
24
|
+
result = JPush::PushResult.new
|
25
|
+
wrapper = @httpclient.sendPost(@@PUSH_API_URL, json_data, autoCode)
|
26
|
+
result.fromResponse(wrapper)
|
27
|
+
return result
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
path = File.expand_path('../', __FILE__)
|
2
|
+
require File.join(path, 'http_client.rb')
|
3
|
+
require File.join(path, 'push_client.rb')
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module JPush
|
7
|
+
class ReportClient
|
8
|
+
@@REPORT_HOST_NAME = 'https://report.jpush.cn'
|
9
|
+
@@REPORT_RECEIVE_PATH = '/v3/received'
|
10
|
+
@@REPORT_USER_PATH = '/v3/users'
|
11
|
+
@@REPORT_MESSAGE_PATH = '/v3/messages'
|
12
|
+
def initialize(maxRetryTimes)
|
13
|
+
@httpclient=JPush::NativeHttpClient.new(maxRetryTimes)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def getReceiveds(msg_ids, authcode)
|
18
|
+
msg_ids = checkMsgids(msg_ids)
|
19
|
+
@url = @@REPORT_HOST_NAME + @@REPORT_RECEIVE_PATH + '?msg_ids=' + msg_ids
|
20
|
+
result = JPush::ReceivedsResult.new
|
21
|
+
wrapper = @httpclient.sendGet(@url, nil, authcode)
|
22
|
+
return result.fromResponse(wrapper)
|
23
|
+
end
|
24
|
+
|
25
|
+
def getMessages(msg_ids, authcode)
|
26
|
+
msg_ids = checkMsgids(msg_ids)
|
27
|
+
@url = @@REPORT_HOST_NAME + @@REPORT_MESSAGE_PATH + '?msg_ids=' + msg_ids
|
28
|
+
return @httpclient.sendGet(@url, nil, authcode)
|
29
|
+
end
|
30
|
+
|
31
|
+
def getUsers(timeUnit, start, duration, authcode)
|
32
|
+
@url = @@REPORT_HOST_NAME + @@REPORT_USER_PATH + '?time_unit=' + timeUnit + '&start=' + start + '&duration=' + duration.to_s
|
33
|
+
return @httpclient.sendGet(@url, nil, authcode)
|
34
|
+
end
|
35
|
+
|
36
|
+
def checkMsgids(msg_ids)
|
37
|
+
if msg_ids.empty?
|
38
|
+
raise ArgumentError.new('msgIds param is required')
|
39
|
+
end
|
40
|
+
msg_ids = msg_ids.split.join('').to_s
|
41
|
+
return msg_ids
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module JPush
|
2
|
+
class ResponseWrapper
|
3
|
+
attr_accessor :rateLimitQuota, :rateLimitRemaining, :rateLimitReset, :code, :error, :responseContent
|
4
|
+
def initialize()
|
5
|
+
|
6
|
+
@logger = Logger.new(STDOUT)
|
7
|
+
end
|
8
|
+
def setRateLimit(quota, remaining, reset)
|
9
|
+
if quota.class != Fixnum
|
10
|
+
raise ArgumentError.new('quota is not FIXnum')
|
11
|
+
end
|
12
|
+
if remaining.class != Fixnum
|
13
|
+
raise ArgumentError.new('remaining is not FIXnum')
|
14
|
+
end
|
15
|
+
if reset.class != Fixnum
|
16
|
+
raise ArgumentError.new('reset is not FIXnum')
|
17
|
+
end
|
18
|
+
@quota = quota
|
19
|
+
@remaining = remaining
|
20
|
+
@reset = reset
|
21
|
+
@logger.debug("JPush API Rate Limiting params - quota:" + quota.to_s + ", remaining:" + remaining.to_s + ", reset:" + reset.to_s)
|
22
|
+
end
|
23
|
+
|
24
|
+
def setResponseContent(content)
|
25
|
+
@responseContent = content
|
26
|
+
end
|
27
|
+
|
28
|
+
def getResponseContent
|
29
|
+
return @responseContent
|
30
|
+
end
|
31
|
+
|
32
|
+
def setErrorObject
|
33
|
+
@error = JSON.parse @responseContent
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'jpush'
|
2
|
+
require './base_remote_tests.rb';
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
class AlertOverrideTests < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@client = JPush::JPushClient.new(AppKey, MasterSecret)
|
8
|
+
end
|
9
|
+
|
10
|
+
def testsendAlert_all
|
11
|
+
@payload = JPush::PushPayload.build(
|
12
|
+
audience: JPush::Audience.all,
|
13
|
+
platform: JPush::Platform.all,
|
14
|
+
notification: JPush::Notification.build(
|
15
|
+
alert: 'alert',
|
16
|
+
ios: JPush::IOSNotification.build(
|
17
|
+
alert: 'ios alert'),
|
18
|
+
android: JPush::AndroidNotification.build(
|
19
|
+
alert: 'android alert'),
|
20
|
+
winphone: JPush::WinphoneNotification.build(
|
21
|
+
alert: 'winphone alert')))
|
22
|
+
res = @client.sendPush(@payload)
|
23
|
+
assert(res.isok, message = "response error")
|
24
|
+
end
|
25
|
+
|
26
|
+
def testsendAlert_android
|
27
|
+
@payload = JPush::PushPayload.build(
|
28
|
+
audience: JPush::Audience.all,
|
29
|
+
platform: JPush::Platform.all,
|
30
|
+
notification: JPush::Notification.build(
|
31
|
+
alert: 'alert',
|
32
|
+
android: JPush::AndroidNotification.build(
|
33
|
+
alert: 'android alert')))
|
34
|
+
res = @client.sendPush(@payload)
|
35
|
+
assert(res.isok, message = "response error")
|
36
|
+
end
|
37
|
+
|
38
|
+
def testsendAlert_ios
|
39
|
+
@payload = JPush::PushPayload.build(
|
40
|
+
audience: JPush::Audience.all,
|
41
|
+
platform: JPush::Platform.all,
|
42
|
+
notification: JPush::Notification.build(
|
43
|
+
alert: 'alert',
|
44
|
+
ios: JPush::IOSNotification.build(
|
45
|
+
alert: 'ios alert')))
|
46
|
+
res = @client.sendPush(@payload)
|
47
|
+
assert(res.isok, message = "response error")
|
48
|
+
end
|
49
|
+
|
50
|
+
def testsendAlert_wp
|
51
|
+
@payload = JPush::PushPayload.build(
|
52
|
+
audience: JPush::Audience.all,
|
53
|
+
platform: JPush::Platform.all,
|
54
|
+
notification: JPush::Notification.build(
|
55
|
+
alert: 'alert',
|
56
|
+
winphone: JPush::WinphoneNotification.build(
|
57
|
+
alert: 'winphone alert')))
|
58
|
+
res = @client.sendPush(@payload)
|
59
|
+
assert(res.isok, message = "response error")
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'jpush'
|
2
|
+
|
3
|
+
require './base_remote_tests.rb'
|
4
|
+
|
5
|
+
require 'test/unit'
|
6
|
+
class AudienceTests < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@client = JPush::JPushClient.new(AppKey, MasterSecret)
|
9
|
+
end
|
10
|
+
|
11
|
+
def testsendByTag
|
12
|
+
@payload = JPush::PushPayload.build(
|
13
|
+
platform: JPush::Platform.all,
|
14
|
+
notification: JPush::Notification.build(
|
15
|
+
alert: 'alert'),
|
16
|
+
audience: JPush::Audience.build(
|
17
|
+
tag: TAG1))
|
18
|
+
res = @client.sendPush(@payload)
|
19
|
+
assert(res.isok, message = "")
|
20
|
+
end
|
21
|
+
|
22
|
+
def testsendByTagAnd
|
23
|
+
@payload = JPush::PushPayload.build(
|
24
|
+
platform: JPush::Platform.all,
|
25
|
+
notification: JPush::Notification.build(
|
26
|
+
alert: 'alert'),
|
27
|
+
audience: JPush::Audience.build(
|
28
|
+
tag_and: TAG1))
|
29
|
+
res = @client.sendPush(@payload)
|
30
|
+
assert(res.isok, message = "")
|
31
|
+
end
|
32
|
+
|
33
|
+
def testsendByAlias
|
34
|
+
@payload = JPush::PushPayload.build(
|
35
|
+
platform: JPush::Platform.all,
|
36
|
+
notification: JPush::Notification.build(
|
37
|
+
alert: 'alert'),
|
38
|
+
audience: JPush::Audience.build(
|
39
|
+
_alias: ALIAS1))
|
40
|
+
res = @client.sendPush(@payload)
|
41
|
+
assert_not_nil(res.isok, message = "")
|
42
|
+
end
|
43
|
+
|
44
|
+
def testsendByRegistrationID
|
45
|
+
@payload = JPush::PushPayload.build(
|
46
|
+
platform: JPush::Platform.all,
|
47
|
+
notification: JPush::Notification.build(
|
48
|
+
alert: 'alert'),
|
49
|
+
audience: JPush::Audience.build(
|
50
|
+
registration_id: REGISTRATION_ID1))
|
51
|
+
res = @client.sendPush(@payload)
|
52
|
+
assert(res.isok, message = "")
|
53
|
+
end
|
54
|
+
|
55
|
+
def testsendByTagMore
|
56
|
+
@payload = JPush::PushPayload.build(
|
57
|
+
platform: JPush::Platform.all,
|
58
|
+
notification: JPush::Notification.build(
|
59
|
+
alert: 'alert'),
|
60
|
+
audience: JPush::Audience.build(
|
61
|
+
tag: TAG2))
|
62
|
+
res = @client.sendPush(@payload)
|
63
|
+
assert(res.isok, message = "")
|
64
|
+
end
|
65
|
+
|
66
|
+
def testsendByTagAndMore
|
67
|
+
@payload = JPush::PushPayload.build(
|
68
|
+
platform: JPush::Platform.all,
|
69
|
+
notification: JPush::Notification.build(
|
70
|
+
alert: 'alert'),
|
71
|
+
audience: JPush::Audience.build(
|
72
|
+
tag_and: TagAndMore))
|
73
|
+
res = @client.sendPush(@payload)
|
74
|
+
assert(res.isok, message = "")
|
75
|
+
end
|
76
|
+
|
77
|
+
def testsendByTagAndMore_fail
|
78
|
+
@payload = JPush::PushPayload.build(
|
79
|
+
platform: JPush::Platform.all,
|
80
|
+
notification: JPush::Notification.build(
|
81
|
+
alert: 'alert'),
|
82
|
+
audience: JPush::Audience.build(
|
83
|
+
tag_and: TAG2))
|
84
|
+
res = @client.sendPush(@payload)
|
85
|
+
assert(res.isok, message = "")
|
86
|
+
end
|
87
|
+
|
88
|
+
def testsendByAliasMore
|
89
|
+
@payload = JPush::PushPayload.build(
|
90
|
+
platform: JPush::Platform.all,
|
91
|
+
notification: JPush::Notification.build(
|
92
|
+
alert: 'alert'),
|
93
|
+
audience: JPush::Audience.build(
|
94
|
+
_alias: ALIAS2))
|
95
|
+
res = @client.sendPush(@payload)
|
96
|
+
assert(res.isok, message = "")
|
97
|
+
end
|
98
|
+
|
99
|
+
def testsendByRegistrationIDMore
|
100
|
+
@payload = JPush::PushPayload.build(
|
101
|
+
platform: JPush::Platform.all,
|
102
|
+
notification: JPush::Notification.build(
|
103
|
+
alert: 'alert'),
|
104
|
+
audience: JPush::Audience.build(
|
105
|
+
registration_id: REGISTRATION_ID2))
|
106
|
+
res = @client.sendPush(@payload)
|
107
|
+
assert(res.isok, message = "")
|
108
|
+
end
|
109
|
+
|
110
|
+
def testsendByTagAlias
|
111
|
+
@payload = JPush::PushPayload.build(
|
112
|
+
platform: JPush::Platform.all,
|
113
|
+
notification: JPush::Notification.build(
|
114
|
+
alert: 'alert'),
|
115
|
+
audience: JPush::Audience.build(
|
116
|
+
tag: TAG_ALL,
|
117
|
+
_alias: ALIAS1))
|
118
|
+
res = @client.sendPush(@payload)
|
119
|
+
assert(res.isok, message = "")
|
120
|
+
end
|
121
|
+
|
122
|
+
def testsendByTagRegistrationID
|
123
|
+
@payload = JPush::PushPayload.build(
|
124
|
+
platform: JPush::Platform.all,
|
125
|
+
notification: JPush::Notification.build(
|
126
|
+
alert: 'alert'),
|
127
|
+
audience: JPush::Audience.build(
|
128
|
+
registration_id: REGISTRATION_ID1,
|
129
|
+
tag: TAG_ALL))
|
130
|
+
res = @client.sendPush(@payload)
|
131
|
+
assert(res.isok, message = "")
|
132
|
+
end
|
133
|
+
|
134
|
+
def testsendByTagRegistrationID_0
|
135
|
+
@payload = JPush::PushPayload.build(
|
136
|
+
platform: JPush::Platform.all,
|
137
|
+
notification: JPush::Notification.build(
|
138
|
+
alert: 'alert'),
|
139
|
+
audience: JPush::Audience.build(
|
140
|
+
registration_id: REGISTRATION_ID1,
|
141
|
+
tag: TAG_NO))
|
142
|
+
res = @client.sendPush(@payload)
|
143
|
+
assert(res.isok, message = "")
|
144
|
+
end
|
145
|
+
|
146
|
+
def testsendByTagAlias_0
|
147
|
+
@payload = JPush::PushPayload.build(
|
148
|
+
platform: JPush::Platform.all,
|
149
|
+
notification: JPush::Notification.build(
|
150
|
+
alert: 'alert'),
|
151
|
+
audience: JPush::Audience.build(
|
152
|
+
tag: TAG3,
|
153
|
+
_alias: ALIAS1))
|
154
|
+
res = @client.sendPush(@payload)
|
155
|
+
assert(res.isok, message = "")
|
156
|
+
end
|
157
|
+
|
158
|
+
def testsendByTagAlias_0_2
|
159
|
+
@payload = JPush::PushPayload.build(
|
160
|
+
platform: JPush::Platform.all,
|
161
|
+
notification: JPush::Notification.build(
|
162
|
+
alert: 'alert'),
|
163
|
+
audience: JPush::Audience.build(
|
164
|
+
tag: TAG_ALL,
|
165
|
+
_alias: ALIAS_NO))
|
166
|
+
res = @client.sendPush(@payload)
|
167
|
+
assert(res.isok, message = "")
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
AppKey ="dd1066407b044738b6479275"
|
3
|
+
MasterSecret = "2b38ce69b1de2a7fa95706ea"
|
4
|
+
|
5
|
+
CONTENT_TYPE_JSON = "application/json"
|
6
|
+
|
7
|
+
SUCCEED_RESULT_CODE = 0
|
8
|
+
LACK_OF_PARAMS = 1002
|
9
|
+
INVALID_PARAMS = 1003
|
10
|
+
AUTHENTICATION_FAIL = 1004
|
11
|
+
TOO_BIG = 1005
|
12
|
+
APPKEY_NOT_EXIST = 1008
|
13
|
+
NO_TARGET = 1011
|
14
|
+
|
15
|
+
ALERT = "JPush Test - alert"
|
16
|
+
MSG_CONTENT = "JPush Test - msgContent"
|
17
|
+
TagAndMore = ["tag1", "tag_all"]
|
18
|
+
TAG1 = ["tag1"]
|
19
|
+
TAG2 = ["tag1", "tag2"]
|
20
|
+
TAG3 = ["tag2"]
|
21
|
+
TAG_ALL = ["tag_all"]
|
22
|
+
TAG_NO = ["tag_no"]
|
23
|
+
ALIAS1 = ["alias1"]
|
24
|
+
ALIAS2 = ["alias1", "alias2"]
|
25
|
+
ALIAS_NO = ["alias_no"]
|
26
|
+
REGISTRATION_ID1 = ["0900e8d85ef"]
|
27
|
+
REGISTRATION_ID2 = ["0900e8d85ef", "0a04ad7d8b4"]
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'jpush'
|
2
|
+
|
3
|
+
require './base_remote_tests.rb'
|
4
|
+
|
5
|
+
require 'test/unit'
|
6
|
+
|
7
|
+
class MessageTests < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@client = JPush::JPushClient.new(AppKey, MasterSecret)
|
10
|
+
end
|
11
|
+
|
12
|
+
def testsendMessageContentOnly
|
13
|
+
@payload = JPush::PushPayload.build(
|
14
|
+
audience: JPush::Audience.all,
|
15
|
+
platform: JPush::Platform.all,
|
16
|
+
message: JPush::Message.build(
|
17
|
+
msg_content: MSG_CONTENT))
|
18
|
+
res = @client.sendPush(@payload)
|
19
|
+
assert(res.isok, message = "")
|
20
|
+
end
|
21
|
+
|
22
|
+
def testsendMessageContentAndTitle
|
23
|
+
@payload = JPush::PushPayload.build(
|
24
|
+
audience: JPush::Audience.all,
|
25
|
+
platform: JPush::Platform.all,
|
26
|
+
message: JPush::Message.build(
|
27
|
+
msg_content: MSG_CONTENT,
|
28
|
+
content_type: "content type",
|
29
|
+
title: "message title"))
|
30
|
+
res = @client.sendPush(@payload)
|
31
|
+
assert(res.isok, message = "")
|
32
|
+
end
|
33
|
+
|
34
|
+
def testsendMessageContentAndExtras
|
35
|
+
array = {}
|
36
|
+
array['key1'] = 'value1'
|
37
|
+
array['key2'] = 'value2'
|
38
|
+
array['key3'] = 'value3'
|
39
|
+
@payload = JPush::PushPayload.build(
|
40
|
+
audience: JPush::Audience.all,
|
41
|
+
platform: JPush::Platform.all,
|
42
|
+
message: JPush::Message.build(
|
43
|
+
msg_content: MSG_CONTENT,
|
44
|
+
extras: array))
|
45
|
+
res = @client.sendPush(@payload)
|
46
|
+
assert(res.isok, message = "")
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'jpush'
|
2
|
+
|
3
|
+
require './base_remote_tests.rb'
|
4
|
+
|
5
|
+
require 'test/unit'
|
6
|
+
class NotificationTests < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@client = JPush::JPushClient.new(AppKey, MasterSecret)
|
9
|
+
end
|
10
|
+
|
11
|
+
def testsendNotification_android_title
|
12
|
+
@payload = JPush::PushPayload.build(
|
13
|
+
audience: JPush::Audience.all,
|
14
|
+
platform: JPush::Platform.all,
|
15
|
+
notification: JPush::Notification.build(
|
16
|
+
android: JPush::AndroidNotification.build(
|
17
|
+
alert: 'alert',
|
18
|
+
title: 'title')))
|
19
|
+
res = @client.sendPush(@payload)
|
20
|
+
assert(res.isok, message = "")
|
21
|
+
end
|
22
|
+
|
23
|
+
def testsendNotification_android_buildId
|
24
|
+
@payload = JPush::PushPayload.build(
|
25
|
+
audience: JPush::Audience.all,
|
26
|
+
platform: JPush::Platform.all,
|
27
|
+
notification: JPush::Notification.build(
|
28
|
+
android: JPush::AndroidNotification.build(
|
29
|
+
alert: 'alert',
|
30
|
+
builder_id: 100)))
|
31
|
+
res = @client.sendPush(@payload)
|
32
|
+
assert(res.isok, message = "")
|
33
|
+
end
|
34
|
+
|
35
|
+
def testsendNotification_android_extras
|
36
|
+
array = {}
|
37
|
+
array['key1'] = 'value1'
|
38
|
+
array['key2'] = 'value2'
|
39
|
+
array['key3'] = 'value3'
|
40
|
+
@payload = JPush::PushPayload.build(
|
41
|
+
audience: JPush::Audience.all,
|
42
|
+
platform: JPush::Platform.all,
|
43
|
+
notification: JPush::Notification.build(
|
44
|
+
android: JPush::AndroidNotification.build(
|
45
|
+
alert: 'alert',
|
46
|
+
extras: array)))
|
47
|
+
res = @client.sendPush(@payload)
|
48
|
+
assert(res.isok, message = "")
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
end
|