mercurius 0.0.1 → 0.0.2
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 +4 -4
- data/.rspec +1 -0
- data/.ruby-version +1 -1
- data/lib/mercurius/apns/connection.rb +37 -0
- data/lib/mercurius/apns/notification.rb +30 -30
- data/lib/mercurius/apns/pem.rb +22 -0
- data/lib/mercurius/apns/service.rb +62 -0
- data/lib/mercurius/apns.rb +10 -0
- data/lib/mercurius/errors/pem_not_configured_error.rb +7 -0
- data/lib/mercurius/errors/pem_not_found_error.rb +7 -0
- data/lib/mercurius/errors/too_many_retries_error.rb +7 -0
- data/lib/mercurius/gcm/connection.rb +24 -0
- data/lib/mercurius/gcm/notification.rb +16 -40
- data/lib/mercurius/gcm/response.rb +35 -0
- data/lib/mercurius/gcm/result.rb +27 -0
- data/lib/mercurius/gcm/service.rb +36 -0
- data/lib/mercurius/gcm.rb +9 -0
- data/lib/mercurius/version.rb +1 -1
- data/lib/mercurius.rb +25 -3
- data/mercurius.gemspec +5 -1
- data/spec/lib/apns_service_spec.rb +70 -0
- data/spec/lib/gcm_service_spec.rb +72 -0
- data/spec/lib/notification_spec.rb +20 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/apns.pem +61 -0
- data/spec/support/fake_socket.rb +30 -0
- metadata +83 -10
- data/lib/mercurius/android.rb +0 -2
- data/lib/mercurius/apns/core.rb +0 -128
- data/lib/mercurius/apple.rb +0 -2
- data/lib/mercurius/gcm/core.rb +0 -113
- data/spec/lib/mercurius_spec.rb +0 -148
@@ -0,0 +1,20 @@
|
|
1
|
+
describe APNS::Notification do
|
2
|
+
|
3
|
+
describe '#==' do
|
4
|
+
it 'doesnt care about object equality' do
|
5
|
+
a = APNS::Notification.new(alert: '1', badge: 1, other: { a: :b }, sound: 'default')
|
6
|
+
b = APNS::Notification.new(alert: '1', badge: 1, other: { a: :b }, sound: 'default')
|
7
|
+
expect(a == b).to eq true
|
8
|
+
end
|
9
|
+
|
10
|
+
%w(alert badge other sound).each do |attr|
|
11
|
+
it "must match #{attr} to be considered equal" do
|
12
|
+
attributes = { alert: '1', badge: 1, other: { a: :b }, sound: 'default' }
|
13
|
+
a = APNS::Notification.new(attributes)
|
14
|
+
b = APNS::Notification.new(attributes.merge(attr => SecureRandom.hex))
|
15
|
+
expect(a == b).to eq false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,61 @@
|
|
1
|
+
Bag Attributes
|
2
|
+
friendlyName: Fake APNs Provider Certificate
|
3
|
+
localKeyID: C0 9A 0E C0 77 F5 7A A4 71 1B 07 9D 10 0E 2D FB 8C B5 62 BC
|
4
|
+
subject=/CN=Fake APNs Provider Certificate/O=Fake Provider/OU=Fake Notification Sending Division/ST=Fake Tennessee/C=US/L=Fake Franklin/emailAddress=support@metova.com
|
5
|
+
issuer=/CN=Fake APNs Certificate Authority/O=Fake Apple/OU=Fake Certificate Signing Division/ST=Fake California/C=US/L=Fake Cupertino/emailAddress=support@metova.com
|
6
|
+
-----BEGIN CERTIFICATE-----
|
7
|
+
MIIENDCCAxygAwIBAgIBAjALBgkqhkiG9w0BAQswgcoxKDAmBgNVBAMMH0Zha2Ug
|
8
|
+
QVBOcyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEzARBgNVBAoMCkZha2UgQXBwbGUx
|
9
|
+
KjAoBgNVBAsMIUZha2UgQ2VydGlmaWNhdGUgU2lnbmluZyBEaXZpc2lvbjEYMBYG
|
10
|
+
A1UECAwPRmFrZSBDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzEXMBUGA1UEBwwORmFr
|
11
|
+
ZSBDdXBlcnRpbm8xHTAbBgkqhkiG9w0BCQEWDmluZm9Ad2F2aWkuY29tMB4XDTEy
|
12
|
+
MDQwNDIwNTYzNloXDTEzMDQwNDIwNTYzNlowgcsxJzAlBgNVBAMMHkZha2UgQVBO
|
13
|
+
cyBQcm92aWRlciBDZXJ0aWZpY2F0ZTEWMBQGA1UECgwNRmFrZSBQcm92aWRlcjEr
|
14
|
+
MCkGA1UECwwiRmFrZSBOb3RpZmljYXRpb24gU2VuZGluZyBEaXZpc2lvbjEYMBYG
|
15
|
+
A1UECAwPRmFrZSBXYXNoaW5ndG9uMQswCQYDVQQGEwJVUzEVMBMGA1UEBwwMRmFr
|
16
|
+
ZSBTZWF0dGxlMR0wGwYJKoZIhvcNAQkBFg5pbmZvQHdhdmlpLmNvbTCCASIwDQYJ
|
17
|
+
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALeeRtyoGCoyTZPLr4mjbK8nQpbwcf6/
|
18
|
+
2GlkqDDaYy17kFjZXA3ZrdPncWnC1gA+93ZAtvy/a1PdSyKMNrTdkqmiId4EzRl5
|
19
|
+
oEFVAEXYSy5DmL78KiMYfmhGXtusq+mj0HvsBHRRsTESqmRhwHj8Th4wYBp6w4jr
|
20
|
+
UmFDIP2FuTqck1WgtgsVQMgVgrgyUUDCzejMfGxBkvavP9TRmYqlZPqRkadMoads
|
21
|
+
T+JTA8JAa7srtQVmdfCg0PvIKZxegIAiGjdoO0ZoWSlS2fWFzDKfsIybXbqinO/s
|
22
|
+
Og5C9ITAYR5+A/pBYXv+R3MOvYLxZ4SgHFOqs4J0oF26324jovt1FWkCAwEAAaMk
|
23
|
+
MCIwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEB
|
24
|
+
CwUAA4IBAQCfsBLbFlCfMFA1t5o4C1QWMA3tfM+MyGUDSJ4qIWFF1/zBQl8bSTc/
|
25
|
+
XkUaNZ42R/NZ+anwyPlYNK/N7JBC5nJiIDkn+SF/ifXUMZcJ6ryWwEIuM9oPf+yM
|
26
|
+
LABztG23gD6RbFw5n3PhO2IsO8dKa2HS4yxBPWAJuOnn9A+H87G+Vy7f7hc9ZgR+
|
27
|
+
BmUNVj9Wm+NBrZrWU431HbLUyi2udD2vDfXk2LopBUN05iOD7BqHEUbdeQ5bPxe5
|
28
|
+
7smdpmEV3QrbpfCfWtoV28OJ/EA0qB0GwihJ3Pkx3V7SaASOwEwhhiwHQDPN/Ksy
|
29
|
+
lG47dCmL1Q47TN9Rzv7LnCSuKXuEycxo
|
30
|
+
-----END CERTIFICATE-----
|
31
|
+
Bag Attributes
|
32
|
+
friendlyName: Fake APNs Provider Certificate
|
33
|
+
localKeyID: C0 9A 0E C0 77 F5 7A A4 71 1B 07 9D 10 0E 2D FB 8C B5 62 BC
|
34
|
+
Key Attributes: <No Attributes>
|
35
|
+
-----BEGIN RSA PRIVATE KEY-----
|
36
|
+
MIIEowIBAAKCAQEAt55G3KgYKjJNk8uviaNsrydClvBx/r/YaWSoMNpjLXuQWNlc
|
37
|
+
Ddmt0+dxacLWAD73dkC2/L9rU91LIow2tN2SqaIh3gTNGXmgQVUARdhLLkOYvvwq
|
38
|
+
Ixh+aEZe26yr6aPQe+wEdFGxMRKqZGHAePxOHjBgGnrDiOtSYUMg/YW5OpyTVaC2
|
39
|
+
CxVAyBWCuDJRQMLN6Mx8bEGS9q8/1NGZiqVk+pGRp0yhp2xP4lMDwkBruyu1BWZ1
|
40
|
+
8KDQ+8gpnF6AgCIaN2g7RmhZKVLZ9YXMMp+wjJtduqKc7+w6DkL0hMBhHn4D+kFh
|
41
|
+
e/5Hcw69gvFnhKAcU6qzgnSgXbrfbiOi+3UVaQIDAQABAoIBAAKv1zudXhUn/Uif
|
42
|
+
X2c1M/7wJSJOTGy84+7O4UMtvuvIdhlGvPka6VdDeL5icn04bqiVU9go+OoWP+Y2
|
43
|
+
hQpqf53p2HMGQPYReI3cL4/WFWuM46xPxlITJq5h8TtnsHBPzFoz3vDQzTX5nvKv
|
44
|
+
F4DtuDrq3E0m5LuZLfBsagwrq8U2xPzIQaRVfl+BGATOFBa30hSwNByqMHW3AuMd
|
45
|
+
5qZQAAXa5C2OhWTEV5sfqQT3hJN/fHgn2gSOi3R3CW8F9yEx0M4ECR+kYb0NdYt/
|
46
|
+
FczNtgnrneK60yhQnOkX19Tr5MGvj44GryLndxAkrFTp5ipfBm+Gc26XKNkfoWJ+
|
47
|
+
ovzT1n0CgYEA2Q5umhJudO3W13WsKOxoU7ErtewemzDpYb0CrxdI9/i9Fxi1QV7C
|
48
|
+
+JSSf0XFKu+nW50D8jMJ60WwU4WX4v8DoG3M0vrXiJ0CZyIClFWgvk+VWi0hGh9b
|
49
|
+
e3PcjNMIKQNK2FyOnOwUnL0DerNFMtYD1kLkDS5CsjGY4VYGPVFRx1sCgYEA2JAB
|
50
|
+
rdUcmN3bfixXeHedRGzw6YFkEbEXqKt8Oa0dVlsDL20JX/Cj5xAESIXyswsIfuAo
|
51
|
+
Rjv4mI/J6lxXhoEiNo/9VsNJ+QH77zv8mVPqs1Z5pxjtp1lNag9jOMZZAX6vgJ1e
|
52
|
+
Glx/stLxxkIhX5aI1ppIZXNFCIY7e8qx9OPYNYsCgYBAw/imimdhBnK7mYYEM/4x
|
53
|
+
MsVLJChBwEdy/XXmjrkcHKE3Wg0Dc2YTzCkmRsg9NoMmZ+R8iVQlWobxdQRMHDGV
|
54
|
+
1g1uyqAzSD4mTdSdNCuhfZef76VX6RFf9cNSVcmyiyPzCTMYiazg09lM7F7yez9r
|
55
|
+
2Eu5zxrhlsD6Q7Pa6f/PzwKBgQCitPd9+mrk83MQxhHmAcCYA12xorRjknFbGZRY
|
56
|
+
5Rbwpaqr/DkZ6vvKA7+kGXdyS6zTXmkD89TgEtA6k6xy8xyYb/CAOnOecX4ujRxI
|
57
|
+
usLQgP0frVwI6tB+ChebNXLz65HCiPmFxs9utIxQT6kxXzFqQzp3dnZ8ZBXW+UBg
|
58
|
+
5Lqx5wKBgFIi2DP5/cF5nUDqOtiQTzl0MeJWW3w3UBS9jQF7Cx00YyLSqIF1batv
|
59
|
+
Zk0Yq5w/2oh9C5EDrf8eyCYfK3iU2Ij4Jy6N6zITjRhW/kxxZigjmcTb/Tsme2Xh
|
60
|
+
IFo5RoAfOV1dPiXM4oqZFPNmppAxiZH2ohEmo593DHYosQHMjXYG
|
61
|
+
-----END RSA PRIVATE KEY-----
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class FakeSocket < StringIO
|
2
|
+
attr_reader :wrote
|
3
|
+
|
4
|
+
def initialize(*)
|
5
|
+
@_open = false
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def write(data)
|
10
|
+
@wrote ||= []
|
11
|
+
@wrote << data
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def read
|
16
|
+
end
|
17
|
+
|
18
|
+
def connect
|
19
|
+
@_open = true
|
20
|
+
end
|
21
|
+
|
22
|
+
def close
|
23
|
+
@_open = false
|
24
|
+
end
|
25
|
+
|
26
|
+
def closed?
|
27
|
+
@_open == false
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mercurius
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Beck
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-03-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: json
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: faraday
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -38,6 +38,48 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: faraday_middleware
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activemodel
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 4.0.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 4.0.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: activesupport
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 4.1.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 4.1.0
|
41
83
|
- !ruby/object:Gem::Dependency
|
42
84
|
name: rake
|
43
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +108,20 @@ dependencies:
|
|
66
108
|
- - ">="
|
67
109
|
- !ruby/object:Gem::Version
|
68
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: webmock
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
69
125
|
description: |2
|
70
126
|
This gem is a wrapper to send push notifications to devices through native push services.
|
71
127
|
email:
|
@@ -75,6 +131,7 @@ extensions: []
|
|
75
131
|
extra_rdoc_files: []
|
76
132
|
files:
|
77
133
|
- ".gitignore"
|
134
|
+
- ".rspec"
|
78
135
|
- ".ruby-gemset"
|
79
136
|
- ".ruby-version"
|
80
137
|
- ".travis.yml"
|
@@ -83,16 +140,28 @@ files:
|
|
83
140
|
- README.md
|
84
141
|
- Rakefile
|
85
142
|
- lib/mercurius.rb
|
86
|
-
- lib/mercurius/
|
87
|
-
- lib/mercurius/apns/
|
143
|
+
- lib/mercurius/apns.rb
|
144
|
+
- lib/mercurius/apns/connection.rb
|
88
145
|
- lib/mercurius/apns/notification.rb
|
89
|
-
- lib/mercurius/
|
90
|
-
- lib/mercurius/
|
146
|
+
- lib/mercurius/apns/pem.rb
|
147
|
+
- lib/mercurius/apns/service.rb
|
148
|
+
- lib/mercurius/errors/pem_not_configured_error.rb
|
149
|
+
- lib/mercurius/errors/pem_not_found_error.rb
|
150
|
+
- lib/mercurius/errors/too_many_retries_error.rb
|
151
|
+
- lib/mercurius/gcm.rb
|
152
|
+
- lib/mercurius/gcm/connection.rb
|
91
153
|
- lib/mercurius/gcm/notification.rb
|
154
|
+
- lib/mercurius/gcm/response.rb
|
155
|
+
- lib/mercurius/gcm/result.rb
|
156
|
+
- lib/mercurius/gcm/service.rb
|
92
157
|
- lib/mercurius/version.rb
|
93
158
|
- mercurius.gemspec
|
94
|
-
- spec/lib/
|
159
|
+
- spec/lib/apns_service_spec.rb
|
160
|
+
- spec/lib/gcm_service_spec.rb
|
161
|
+
- spec/lib/notification_spec.rb
|
95
162
|
- spec/spec_helper.rb
|
163
|
+
- spec/support/apns.pem
|
164
|
+
- spec/support/fake_socket.rb
|
96
165
|
homepage: https://github.com/jrbeck/mercurius
|
97
166
|
licenses: []
|
98
167
|
metadata: {}
|
@@ -117,5 +186,9 @@ signing_key:
|
|
117
186
|
specification_version: 4
|
118
187
|
summary: Send push notifications to mobile devices through native services
|
119
188
|
test_files:
|
120
|
-
- spec/lib/
|
189
|
+
- spec/lib/apns_service_spec.rb
|
190
|
+
- spec/lib/gcm_service_spec.rb
|
191
|
+
- spec/lib/notification_spec.rb
|
121
192
|
- spec/spec_helper.rb
|
193
|
+
- spec/support/apns.pem
|
194
|
+
- spec/support/fake_socket.rb
|
data/lib/mercurius/android.rb
DELETED
data/lib/mercurius/apns/core.rb
DELETED
@@ -1,128 +0,0 @@
|
|
1
|
-
require 'socket'
|
2
|
-
require 'openssl'
|
3
|
-
require 'json'
|
4
|
-
|
5
|
-
module APNS
|
6
|
-
class Error < Exception; end
|
7
|
-
class ConfigurationError < Error; end
|
8
|
-
|
9
|
-
@host = 'gateway.sandbox.push.apple.com'
|
10
|
-
@port = 2195
|
11
|
-
@pem_path = nil
|
12
|
-
@pem_password = nil
|
13
|
-
@pem_data = nil
|
14
|
-
|
15
|
-
@persistent = false
|
16
|
-
@mutex = Mutex.new
|
17
|
-
@retries = 3 # TODO: check if we really need this
|
18
|
-
|
19
|
-
@sock = nil
|
20
|
-
@ssl = nil
|
21
|
-
|
22
|
-
class << self
|
23
|
-
attr_accessor :host, :port, :pem_path, :pem_password, :pem_data
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.start_persistence
|
27
|
-
@persistent = true
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.stop_persistence
|
31
|
-
@persistent = false
|
32
|
-
self.close_connection
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.send_notification(device_token, message)
|
36
|
-
notification = APNS::Notification.new(device_token, message)
|
37
|
-
send_notifications([notification])
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.send_notifications(notifications)
|
41
|
-
@mutex.synchronize do
|
42
|
-
with_connection do
|
43
|
-
notifications.each do |n|
|
44
|
-
@ssl.write(n.packaged_notification)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.feedback
|
51
|
-
sock, ssl = feedback_connection
|
52
|
-
apns_feedback = []
|
53
|
-
|
54
|
-
# read lines from the socket
|
55
|
-
while line = ssl.read(38)
|
56
|
-
line.strip!
|
57
|
-
f = line.unpack('N1n1H140')
|
58
|
-
apns_feedback << { timestamp: Time.at(f[0]), token: f[2] }
|
59
|
-
end
|
60
|
-
|
61
|
-
ssl.close
|
62
|
-
sock.close
|
63
|
-
|
64
|
-
apns_feedback
|
65
|
-
end
|
66
|
-
|
67
|
-
protected
|
68
|
-
|
69
|
-
def self.with_connection
|
70
|
-
attempts = 1
|
71
|
-
|
72
|
-
begin
|
73
|
-
open_connection if connection_closed?
|
74
|
-
yield
|
75
|
-
rescue StandardError, Errno::EPIPE
|
76
|
-
raise Error.new("Failed after #{@retries} attempts.") unless attempts < @retries
|
77
|
-
close_connection
|
78
|
-
attempts += 1
|
79
|
-
retry
|
80
|
-
end
|
81
|
-
|
82
|
-
close_connection unless @persistent
|
83
|
-
end
|
84
|
-
|
85
|
-
def self.open_connection
|
86
|
-
@sock = TCPSocket.new(self.host, self.port)
|
87
|
-
@ssl = OpenSSL::SSL::SSLSocket.new(@sock, context)
|
88
|
-
@ssl.connect
|
89
|
-
end
|
90
|
-
|
91
|
-
def self.close_connection
|
92
|
-
@ssl.close
|
93
|
-
@ssl = nil
|
94
|
-
@sock.close
|
95
|
-
@sock = nil
|
96
|
-
end
|
97
|
-
|
98
|
-
def self.connection_closed?
|
99
|
-
@ssl.nil? || @sock.nil? || @ssl.closed? || @sock.closed?
|
100
|
-
end
|
101
|
-
|
102
|
-
def self.feedback_connection
|
103
|
-
fhost = self.host.gsub('gateway','feedback')
|
104
|
-
sock = TCPSocket.new(fhost, 2196)
|
105
|
-
ssl = OpenSSL::SSL::SSLSocket.new(sock, context)
|
106
|
-
ssl.connect
|
107
|
-
return sock, ssl
|
108
|
-
end
|
109
|
-
|
110
|
-
def self.context
|
111
|
-
context = OpenSSL::SSL::SSLContext.new
|
112
|
-
context.cert = OpenSSL::X509::Certificate.new(pem_data)
|
113
|
-
context.key = OpenSSL::PKey::RSA.new(pem_data, pem_password)
|
114
|
-
context
|
115
|
-
end
|
116
|
-
|
117
|
-
def self.pem_data
|
118
|
-
return @pem_data if @pem_data
|
119
|
-
|
120
|
-
if @pem_path
|
121
|
-
raise ConfigurationError.new('The specified PEM file does not exist.') unless File.exist?(@pem_path)
|
122
|
-
@pem_data = File.read(@pem_path)
|
123
|
-
else
|
124
|
-
raise ConfigurationError.new('PEM not configured properly.')
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
end
|
data/lib/mercurius/apple.rb
DELETED
data/lib/mercurius/gcm/core.rb
DELETED
@@ -1,113 +0,0 @@
|
|
1
|
-
require 'httparty'
|
2
|
-
require 'json'
|
3
|
-
|
4
|
-
module GCM
|
5
|
-
include HTTParty
|
6
|
-
|
7
|
-
@host = 'https://android.googleapis.com/gcm/send'
|
8
|
-
@format = :json
|
9
|
-
@key = nil
|
10
|
-
|
11
|
-
class << self
|
12
|
-
attr_accessor :host, :format, :key
|
13
|
-
|
14
|
-
def key(identity = nil)
|
15
|
-
if @key.is_a?(Hash)
|
16
|
-
raise %{If your key is a hash of keys you'll need to pass a identifier to the notification!} if identity.nil?
|
17
|
-
return @key[identity]
|
18
|
-
else
|
19
|
-
return @key
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def key_identities
|
24
|
-
if @key.is_a?(Hash)
|
25
|
-
return @key.keys
|
26
|
-
else
|
27
|
-
return nil
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.send_notification(device_tokens, data = {}, options = {})
|
33
|
-
n = GCM::Notification.new(device_tokens, data, options)
|
34
|
-
self.send_notifications([n])
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.send_notifications(notifications)
|
38
|
-
responses = []
|
39
|
-
notifications.each do |n|
|
40
|
-
responses << self.prepare_and_send(n)
|
41
|
-
end
|
42
|
-
responses
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
def self.prepare_and_send(n)
|
48
|
-
if n.device_tokens.count < 1 || n.device_tokens.count > 1000
|
49
|
-
raise "Number of device tokens must be between 1 and 1000"
|
50
|
-
end
|
51
|
-
if !n.collapse_key.nil? && n.time_to_live.nil?
|
52
|
-
raise %q{If you are defining a "collapse key" you need a "time to live"}
|
53
|
-
end
|
54
|
-
if @key.is_a?(Hash) && n.identity.nil?
|
55
|
-
raise %{If your key is a Hash of keys you'll need to pass a identifier to the notification!}
|
56
|
-
end
|
57
|
-
|
58
|
-
if self.format == :json
|
59
|
-
self.send_push_as_json(n)
|
60
|
-
elsif self.format == :text
|
61
|
-
self.send_push_as_plain_text(n)
|
62
|
-
else
|
63
|
-
raise "Invalid format"
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def self.send_push_as_json(n)
|
68
|
-
headers = {
|
69
|
-
'Authorization' => "key=#{ self.key(n.identity) }",
|
70
|
-
'Content-Type' => 'application/json',
|
71
|
-
}
|
72
|
-
body = {
|
73
|
-
registration_ids: n.device_tokens,
|
74
|
-
data: n.data,
|
75
|
-
collapse_key: n.collapse_key,
|
76
|
-
time_to_live: n.time_to_live,
|
77
|
-
delay_while_idle: n.delay_while_idle
|
78
|
-
}
|
79
|
-
return self.send_to_server(headers, body.to_json)
|
80
|
-
end
|
81
|
-
|
82
|
-
def self.send_push_as_plain_text(n)
|
83
|
-
raise "Still has to be done: http://developer.android.com/guide/google/gcm/gcm.html"
|
84
|
-
headers = {
|
85
|
-
# TODO: Aceitar key ser um hash
|
86
|
-
'Authorization' => "key=#{ self.key(n.identity) }",
|
87
|
-
'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8',
|
88
|
-
}
|
89
|
-
return self.send_to_server(headers, body)
|
90
|
-
end
|
91
|
-
|
92
|
-
def self.send_to_server(headers, body)
|
93
|
-
params = { headers: headers, body: body}
|
94
|
-
response = self.post(self.host, params)
|
95
|
-
build_response(response)
|
96
|
-
end
|
97
|
-
|
98
|
-
def self.build_response(response)
|
99
|
-
case response.code
|
100
|
-
when 200
|
101
|
-
{ response: 'success', body: JSON.parse(response.body), headers: response.headers, status_code: response.code }
|
102
|
-
when 400
|
103
|
-
{ response: 'Only applies for JSON requests. Indicates that the request could not be parsed as JSON, or it contained invalid fields.', status_code: response.code }
|
104
|
-
when 401
|
105
|
-
{ response: 'There was an error authenticating the sender account.', status_code: response.code }
|
106
|
-
when 500
|
107
|
-
{ response: 'There was an internal error in the GCM server while trying to process the request.', status_code: response.code }
|
108
|
-
when 503
|
109
|
-
{ response: 'Server is temporarily unavailable.', status_code: response.code }
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
end
|
data/spec/lib/mercurius_spec.rb
DELETED
@@ -1,148 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Mercurius do
|
4
|
-
describe "APNS" do
|
5
|
-
it "should have an APNS object" do
|
6
|
-
defined?(APNS).should_not be_false
|
7
|
-
end
|
8
|
-
|
9
|
-
it "should not forget the APNS default parameters" do
|
10
|
-
APNS.host.should == "gateway.sandbox.push.apple.com"
|
11
|
-
APNS.port.should == 2195
|
12
|
-
APNS.pem_path.should be_equal(nil)
|
13
|
-
APNS.pem_password.should be_equal(nil)
|
14
|
-
end
|
15
|
-
|
16
|
-
describe "Notifications" do
|
17
|
-
describe "#==" do
|
18
|
-
it "should properly equate objects without caring about object identity" do
|
19
|
-
a = APNS::Notification.new("123", {:alert => "hi"})
|
20
|
-
b = APNS::Notification.new("123", {:alert => "hi"})
|
21
|
-
a.should eq(b)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
describe '.send_notification' do
|
27
|
-
let(:token) { 'token' }
|
28
|
-
let(:message) { 'message' }
|
29
|
-
let(:pem_data) { 'pem_data' }
|
30
|
-
let(:cert) { double }
|
31
|
-
let(:key) { double }
|
32
|
-
let(:sock) { double(close: nil) }
|
33
|
-
let(:ssl) { double(connect: nil, write: nil, close: nil) }
|
34
|
-
let(:packaged) { 'packaged' }
|
35
|
-
|
36
|
-
after do
|
37
|
-
APNS.pem_path = nil
|
38
|
-
APNS.pem_data = nil
|
39
|
-
end
|
40
|
-
|
41
|
-
before do
|
42
|
-
allow(OpenSSL::X509::Certificate).to receive(:new).
|
43
|
-
with(pem_data).and_return(cert)
|
44
|
-
allow(OpenSSL::PKey::RSA).to receive(:new).
|
45
|
-
with(pem_data, anything).and_return(key)
|
46
|
-
allow(TCPSocket).to receive(:new).and_return(sock)
|
47
|
-
allow(OpenSSL::SSL::SSLSocket).to receive(:new).and_return(ssl)
|
48
|
-
end
|
49
|
-
|
50
|
-
shared_examples 'notifications' do
|
51
|
-
it 'notifications are sent' do
|
52
|
-
expect(ssl).to have_received(:write).with(/"#{message}"/)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
context 'with pem setting' do
|
57
|
-
context 'with an existing pem file' do
|
58
|
-
let(:path) { '/good/path' }
|
59
|
-
|
60
|
-
before do
|
61
|
-
allow(File).to receive(:exist?).with(path).and_return(true)
|
62
|
-
allow(File).to receive(:read).with(path).and_return(pem_data)
|
63
|
-
end
|
64
|
-
|
65
|
-
before { APNS.pem_path = '/good/path' }
|
66
|
-
before { APNS.send_notification(token, message) }
|
67
|
-
|
68
|
-
include_examples 'notifications'
|
69
|
-
end
|
70
|
-
|
71
|
-
context 'when the pem does not exist' do
|
72
|
-
before { APNS.pem_path = '/bad/path' }
|
73
|
-
|
74
|
-
it 'fails' do
|
75
|
-
expect do
|
76
|
-
APNS.send_notification(token, message)
|
77
|
-
end.to raise_error(APNS::ConfigurationError, /does not exist/)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
context 'with pem_data' do
|
83
|
-
before { APNS.pem_data = pem_data }
|
84
|
-
before { APNS.send_notification(token, message) }
|
85
|
-
|
86
|
-
include_examples 'notifications'
|
87
|
-
end
|
88
|
-
|
89
|
-
context 'without pem or pem_data' do
|
90
|
-
before { APNS.pem_path = nil }
|
91
|
-
|
92
|
-
it 'fails' do
|
93
|
-
expect do
|
94
|
-
APNS.send_notification(token, message)
|
95
|
-
end.to raise_error(APNS::ConfigurationError, /PEM not configured properly/)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
end
|
101
|
-
|
102
|
-
describe "GCM" do
|
103
|
-
it "should have a GCM object" do
|
104
|
-
defined?(GCM).should_not be_false
|
105
|
-
end
|
106
|
-
|
107
|
-
describe "Notifications" do
|
108
|
-
|
109
|
-
before do
|
110
|
-
@options = {:data => "dummy data"}
|
111
|
-
end
|
112
|
-
|
113
|
-
it "should allow only notifications with device_tokens as array" do
|
114
|
-
n = GCM::Notification.new("id", @options)
|
115
|
-
n.device_tokens.is_a?(Array).should be_true
|
116
|
-
|
117
|
-
n.device_tokens = ["a" "b", "c"]
|
118
|
-
n.device_tokens.is_a?(Array).should be_true
|
119
|
-
|
120
|
-
n.device_tokens = "a"
|
121
|
-
n.device_tokens.is_a?(Array).should be_true
|
122
|
-
end
|
123
|
-
|
124
|
-
it "should allow only notifications with data as hash with :data root" do
|
125
|
-
n = GCM::Notification.new("id", { :data => "data" })
|
126
|
-
|
127
|
-
n.data.is_a?(Hash).should be_true
|
128
|
-
n.data.should == {:data => "data"}
|
129
|
-
|
130
|
-
n.data = {:a => ["a", "b", "c"]}
|
131
|
-
n.data.is_a?(Hash).should be_true
|
132
|
-
n.data.should == {:a => ["a", "b", "c"]}
|
133
|
-
|
134
|
-
n.data = {:a => "a"}
|
135
|
-
n.data.is_a?(Hash).should be_true
|
136
|
-
n.data.should == {:a => "a"}
|
137
|
-
end
|
138
|
-
|
139
|
-
describe "#==" do
|
140
|
-
it "should properly equate objects without caring about object identity" do
|
141
|
-
a = GCM::Notification.new("id", { :data => "data" })
|
142
|
-
b = GCM::Notification.new("id", { :data => "data" })
|
143
|
-
a.should eq(b)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|