crusade-apns 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b99d15d9bd5de7752db570201d229fe1ff16e6bd
4
- data.tar.gz: 04bfd747ca356f60c9a03cddf688a5013d203f8c
3
+ metadata.gz: 0586aee6082f8c106b83cfb352e88a0c0a8bb348
4
+ data.tar.gz: 1948a8c7e2baccc2a20c3be98a90f8dca1c830ef
5
5
  SHA512:
6
- metadata.gz: a0447cf0e5df77f18b0ce32df590f95b7ab90be81bea9790f46d60bf87958d4b572cee2af05c9fa2a5d44ff8ea7597e58f5ffbd74a76a1c4bc6832707f2cbe8a
7
- data.tar.gz: 7213bfce39dce624752ab0439b26eb78c3d2283921568b31a69c1fd834e458846eb05e5db813d73b185b2ff515330252f5b7ce4247afae4909fd0a3064195396
6
+ metadata.gz: 2534d6f2fb94d062f5bc90724d3a5c6283fd70230835d5329ab1399c13fc0a789ebf6fe99bc64ecaa90d1e8da108a8528bdec114f17bb40c2fa887980f966168
7
+ data.tar.gz: 758a16b9e8470388a53cca1f91c71f7e533dde02c26ccdfea16f8037fb02f74d0410492c9f925f369727ab36131afb6b1fcca1e2d89d4f5a9670e26bc26e7626
data/.gitignore CHANGED
@@ -16,3 +16,4 @@ test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
18
  .DS_Store
19
+ .ruby-version
@@ -6,4 +6,6 @@ rvm:
6
6
  - 2.0.0
7
7
  matrix:
8
8
  allow_failures:
9
- - rvm: jruby-19mode
9
+ - rvm:
10
+ - jruby-19mode
11
+ - rbx-19mode
@@ -0,0 +1,48 @@
1
+ module Crusade
2
+ module APNS
3
+ class Notification
4
+ attr_accessor :title, :body, :action, :url_args, :device_token
5
+
6
+ def initialize(attrs = {} )
7
+ self.title = attrs.fetch(:title) { nil }
8
+ self.body = attrs.fetch(:body) { nil }
9
+ self.action = attrs.fetch(:action) { nil }
10
+ self.url_args = attrs.fetch(:url_args) { [] }
11
+ self.device_token = attrs.fetch(:device_token) { nil }
12
+ end
13
+
14
+ def to_json
15
+ payload.to_json
16
+ end
17
+
18
+ def json_size
19
+ to_json.size
20
+ end
21
+
22
+ private
23
+
24
+ def payload
25
+ {
26
+ "aps" => aps
27
+ }
28
+ end
29
+
30
+ def alert
31
+ h = {
32
+ "title" => title,
33
+ "body" => body
34
+ }
35
+ h.update "action" => action if action
36
+ h
37
+ end
38
+
39
+ def aps
40
+ h = {
41
+ "alert" => alert,
42
+ }
43
+ h.update "url-args" => url_args if url_args.any?
44
+ h
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,46 @@
1
+ require 'crusade/apns/notification'
2
+
3
+ module Crusade
4
+ module APNS
5
+ class NotificationBuilder
6
+
7
+ def initialize
8
+ self.notification = Crusade::APNS::Notification.new
9
+ end
10
+
11
+ def build
12
+ notification
13
+ end
14
+
15
+ def with_title(title)
16
+ notification.title = title
17
+ self
18
+ end
19
+
20
+ def with_body(body)
21
+ notification.body = body
22
+ self
23
+ end
24
+
25
+ def with_action(action)
26
+ notification.action = action
27
+ self
28
+ end
29
+
30
+ def with_url_args(url_args)
31
+ notification.url_args = url_args
32
+ self
33
+ end
34
+
35
+ def with_device_token(device_token)
36
+ notification.device_token = device_token
37
+ self
38
+ end
39
+
40
+ private
41
+
42
+ attr_accessor :notification
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,38 @@
1
+ module Crusade
2
+ module APNS
3
+ class NotificationEncoder
4
+ def initialize configuration
5
+ self.configuration = configuration
6
+ end
7
+
8
+ def encode notification
9
+ to_binary(notification)
10
+ end
11
+
12
+ private
13
+
14
+ attr_accessor :configuration
15
+
16
+ def to_binary(notification)
17
+ [
18
+ 1,
19
+ id_for_pack,
20
+ expiry,
21
+ 0,
22
+ 32,
23
+ notification.device_token,
24
+ notification.json_size,
25
+ notification.to_json
26
+ ].pack("cNNccH*na*")
27
+ end
28
+
29
+ def expiry
30
+ 86400
31
+ end
32
+
33
+ def id_for_pack
34
+ 0
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,28 @@
1
+ require 'crusade/apns/socket_connection'
2
+
3
+ module Crusade
4
+ module APNS
5
+ class PushNotification
6
+ attr_reader :configuration
7
+
8
+ def initialize(configuration, connection = nil)
9
+ self.configuration = configuration
10
+ self.encoder = NotificationEncoder.new(configuration)
11
+ self.connection = connection
12
+ end
13
+
14
+ def send notification
15
+ connection.send encoder.encode notification
16
+ end
17
+
18
+ private
19
+
20
+ attr_accessor :encoder
21
+ attr_writer :configuration, :connection
22
+
23
+ def connection
24
+ @connection ||= Crusade::APNS::SocketConnection.new(configuration)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -12,6 +12,9 @@ module Crusade
12
12
  end
13
13
 
14
14
  def generate user_id
15
+ FileUtils.remove_entry_secure configuration.temp_dir
16
+ FileUtils.mkdir configuration.temp_dir
17
+
15
18
  push_package = File.join(configuration.temp_dir, file_name(user_id))
16
19
 
17
20
  DirectoryStructureGenerator.new(configuration).generate
@@ -30,8 +33,9 @@ module Crusade
30
33
  [ user_token, push_package ]
31
34
  end
32
35
 
33
- def clean
34
- # FileUtils.remove_entry_secure destination
36
+ def clean(push_package)
37
+ FileUtils.remove_entry_secure push_package
38
+ rescue Errno::ENOENT => e
35
39
  end
36
40
 
37
41
  private
@@ -0,0 +1,54 @@
1
+ module Crusade
2
+ module APNS
3
+ class SocketConnection
4
+ attr_accessor :host, :port
5
+
6
+ def initialize(configuration)
7
+ self.configuration = configuration
8
+
9
+ self.host = 'gateway.push.apple.com'
10
+ self.port = 2195
11
+ end
12
+
13
+ def send data
14
+ write data
15
+ end
16
+
17
+ private
18
+
19
+ attr_accessor :configuration
20
+
21
+ def open_connection
22
+ sock = TCPSocket.new(host, port)
23
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, context)
24
+ ssl.connect
25
+
26
+ return sock, ssl
27
+ end
28
+
29
+ def write binary
30
+ sock, ssl = open_connection
31
+
32
+ ssl.write(binary)
33
+
34
+ ssl.close
35
+ sock.close
36
+ end
37
+
38
+ def context
39
+ ssl_context = OpenSSL::SSL::SSLContext.new
40
+ ssl_context.cert = OpenSSL::X509::Certificate.new(p12_certificate.certificate.to_pem)
41
+ ssl_context.key = OpenSSL::PKey::RSA.new(p12_certificate.key.to_pem)
42
+
43
+ ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
44
+
45
+ ssl_context
46
+ end
47
+
48
+ def p12_certificate
49
+ @p12 ||= OpenSSL::PKCS12.new File.read(configuration.certificate),
50
+ configuration.certificate_password
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,5 +1,5 @@
1
1
  module Crusade
2
2
  module APNS
3
- VERSION = "0.5.0"
3
+ VERSION = "0.6.0"
4
4
  end
5
5
  end
@@ -0,0 +1,43 @@
1
+ require File.expand_path '../../test_helper.rb', __FILE__
2
+
3
+ require 'fixtures'
4
+ require 'fake_apns_server'
5
+ require 'base64'
6
+ require 'timeout'
7
+
8
+ describe Crusade::APNS::PushNotification do
9
+ let(:options) { Fixtures.default_configuration }
10
+ subject { Crusade::APNS::PushNotification.new options, connection }
11
+ let(:connection) { Crusade::APNS::SocketConnection.new options }
12
+
13
+ describe 'send' do
14
+ let(:server) { FakeApnsServer.new options }
15
+
16
+ before do
17
+ connection.host = '127.0.0.1'
18
+ server.start
19
+ end
20
+
21
+ after { server.stop }
22
+
23
+ let(:binary) do
24
+ <<-eof
25
+ AQAAAAAAAVGAACASNKAAXHsiYXBzIjp7ImFsZXJ0Ijp7InRpdGxlIjoidGhl
26
+ IHRpdGxlIiwiYm9keSI6InRoZSBib2R5IiwiYWN0aW9uIjoic3VibWl0In0s
27
+ InVybC1hcmdzIjpbImEiXX19
28
+ eof
29
+ end
30
+
31
+ it 'sends the binary representation of the notification' do
32
+ subject.send Fixtures.default_notification
33
+
34
+ Timeout::timeout(1, ThreadError) { while(server.received.size == 0) ; end }
35
+
36
+ server.received.last.must_equal Base64.decode64 binary
37
+ end
38
+ end
39
+
40
+ describe 'configuration' do
41
+ it { subject.configuration.must_be_same_as options }
42
+ end
43
+ end
@@ -8,15 +8,15 @@ require 'zip_support'
8
8
 
9
9
  describe Crusade::APNS::PushPackageGenerator do
10
10
  let(:options) { Fixtures.default_configuration }
11
- subject { Crusade::APNS::PushPackageGenerator.new options }
11
+ let(:user_id) { '1234' }
12
12
 
13
- describe 'generate' do
14
- let(:user_id) { '1234' }
13
+ let(:generated) { subject.generate user_id }
14
+ let(:push_package) { generated[1] }
15
+ let(:user_token) { generated[0] }
15
16
 
16
- let(:generated) { subject.generate user_id }
17
- let(:push_package) { generated[1] }
18
- let(:user_token) { generated[0] }
17
+ subject { Crusade::APNS::PushPackageGenerator.new options }
19
18
 
19
+ describe 'generate' do
20
20
  let(:tmp_dir) { Dir.mktmpdir }
21
21
 
22
22
  after do
@@ -36,5 +36,26 @@ describe Crusade::APNS::PushPackageGenerator do
36
36
  files.must_equal_contents %w(manifest.json website.json signature icon.iconset/) + icons
37
37
  user_token.wont_be_nil
38
38
  end
39
+
40
+ it 'can generate twice in a row' do
41
+ subject.generate user_id
42
+ subject.generate user_id
43
+
44
+ File.exists?(push_package).must_be_truthy
45
+ end
46
+ end
47
+
48
+ describe 'clean' do
49
+ it 'removes the push package' do
50
+ push_package
51
+
52
+ subject.clean push_package
53
+
54
+ File.exists?(push_package).must_equal false
55
+ end
56
+
57
+ it 'does not blow up if the file does not exist' do
58
+ subject.clean 'foo'
59
+ end
39
60
  end
40
61
  end
@@ -0,0 +1,51 @@
1
+ require "socket"
2
+ require "openssl"
3
+ require "thread"
4
+
5
+ class FakeApnsServer
6
+ attr_reader :received
7
+
8
+ def initialize(configuration)
9
+ self.port = 2195
10
+ self.configuration = configuration
11
+ self.received = []
12
+ end
13
+
14
+ def start
15
+ cert = OpenSSL::PKCS12.new(File.read(configuration.certificate), configuration.certificate_password)
16
+
17
+ server = TCPServer.new(port)
18
+
19
+ ssl_context = OpenSSL::SSL::SSLContext.new
20
+
21
+ ssl_context.cert = OpenSSL::X509::Certificate.new(cert.certificate.to_pem)
22
+ ssl_context.key = OpenSSL::PKey::RSA.new(cert.key.to_pem)
23
+ ssl_server = OpenSSL::SSL::SSLServer.new(server, ssl_context)
24
+
25
+ launch_thread(ssl_server)
26
+ end
27
+
28
+ def stop
29
+ thread.terminate
30
+ end
31
+
32
+ private
33
+
34
+ attr_accessor :port, :configuration, :thread
35
+ attr_writer :received
36
+
37
+ def launch_thread(ssl_server)
38
+ self.thread = Thread.new {
39
+ loop do
40
+ connection = ssl_server.accept
41
+ while (line_in = connection.gets)
42
+ register_received line_in.chomp
43
+ end
44
+ end
45
+ }
46
+ end
47
+
48
+ def register_received line
49
+ received << line
50
+ end
51
+ end
@@ -1,5 +1,7 @@
1
1
  require 'tmpdir'
2
2
 
3
+ require 'crusade/apns/push_notification'
4
+
3
5
  class Fixtures
4
6
  def self.default_configuration
5
7
  OpenStruct.new({
@@ -15,4 +17,14 @@ class Fixtures
15
17
  certificate_password: 'test'
16
18
  })
17
19
  end
20
+
21
+ def self.default_notification
22
+ Crusade::APNS::Notification.new({
23
+ title: 'the title',
24
+ body: 'the body',
25
+ action: 'submit',
26
+ url_args: ['a'],
27
+ device_token: '1234a'
28
+ })
29
+ end
18
30
  end
@@ -0,0 +1,76 @@
1
+ require File.expand_path '../../test_helper.rb', __FILE__
2
+
3
+ require 'crusade/apns/notification_builder'
4
+
5
+ describe Crusade::APNS::NotificationBuilder do
6
+ let(:described_class) { Crusade::APNS::NotificationBuilder }
7
+ subject { described_class.new }
8
+
9
+ describe 'build' do
10
+ it' creates a notification' do
11
+ notification = subject.build
12
+
13
+ notification.is_a?(Crusade::APNS::Notification).must_be_true
14
+ end
15
+ end
16
+
17
+ describe 'with_title' do
18
+ it 'returns the builder' do
19
+ subject.with_title('test').must_be_same_as subject
20
+ end
21
+
22
+ it 'sets the title of the notification' do
23
+ notif = subject.with_title('test').build
24
+
25
+ notif.title.must_equal 'test'
26
+ end
27
+ end
28
+
29
+ describe 'with_body' do
30
+ it 'returns the builder' do
31
+ subject.with_body('test').must_be_same_as subject
32
+ end
33
+
34
+ it 'sets the body of the notification' do
35
+ notif = subject.with_body('test').build
36
+
37
+ notif.body.must_equal 'test'
38
+ end
39
+ end
40
+
41
+ describe 'with_action' do
42
+ it 'returns the builder' do
43
+ subject.with_action('test').must_be_same_as subject
44
+ end
45
+
46
+ it 'sets the action of the notification' do
47
+ notif = subject.with_action('test').build
48
+
49
+ notif.action.must_equal 'test'
50
+ end
51
+ end
52
+
53
+ describe 'with_url_args' do
54
+ it 'returns the builder' do
55
+ subject.with_url_args('test').must_be_same_as subject
56
+ end
57
+
58
+ it 'sets the url_args of the notification' do
59
+ notif = subject.with_url_args(['test']).build
60
+
61
+ notif.url_args.must_equal ['test']
62
+ end
63
+ end
64
+
65
+ describe 'with_device_token' do
66
+ it 'returns the builder' do
67
+ subject.with_device_token('test').must_be_same_as subject
68
+ end
69
+
70
+ it 'sets the url_args of the notification' do
71
+ notif = subject.with_device_token('test').build
72
+
73
+ notif.device_token.must_equal 'test'
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,29 @@
1
+ require File.expand_path '../../test_helper.rb', __FILE__
2
+
3
+ require 'crusade/apns/notification_encoder'
4
+
5
+ require 'base64'
6
+ require 'fixtures'
7
+
8
+ describe Crusade::APNS::NotificationEncoder do
9
+ let(:described_class) { Crusade::APNS::NotificationEncoder }
10
+
11
+ let(:options) { Fixtures.default_configuration }
12
+
13
+ subject { Crusade::APNS::NotificationEncoder.new options }
14
+
15
+ describe 'encode' do
16
+ let(:notification) { Fixtures.default_notification }
17
+ let(:expected) do
18
+ <<-eof
19
+ AQAAAAAAAVGAACASNKAAXHsiYXBzIjp7ImFsZXJ0Ijp7InRpdGxlIjoidGhl
20
+ IHRpdGxlIiwiYm9keSI6InRoZSBib2R5IiwiYWN0aW9uIjoic3VibWl0In0s
21
+ InVybC1hcmdzIjpbImEiXX19
22
+ eof
23
+ end
24
+
25
+ let(:encoded) { subject.encode notification }
26
+
27
+ it { encoded.must_equal Base64.decode64(expected) }
28
+ end
29
+ end
@@ -0,0 +1,88 @@
1
+ require File.expand_path '../../test_helper.rb', __FILE__
2
+
3
+ require 'crusade/apns/notification'
4
+
5
+ describe Crusade::APNS::Notification do
6
+ let(:described_class) { Crusade::APNS::Notification }
7
+
8
+ let(:attrs) do
9
+ {
10
+ title: 'the title',
11
+ body: 'the body',
12
+ action: 'submit',
13
+ url_args: ['a'],
14
+ device_token: '1234a'
15
+ }
16
+ end
17
+
18
+ describe 'initialize' do
19
+ subject { described_class.new }
20
+
21
+ it 'initializes the title' do
22
+ subject.title.must_be_nil
23
+ end
24
+
25
+ it 'initializes the body' do
26
+ subject.body.must_be_nil
27
+ end
28
+
29
+ it 'initializes the action' do
30
+ subject.action.must_be_nil
31
+ end
32
+
33
+ it 'initializes the url_args' do
34
+ subject.url_args.must_equal []
35
+ end
36
+
37
+ it 'initializes the device_token' do
38
+ subject.device_token.must_be_nil
39
+ end
40
+
41
+ describe 'with a hash' do
42
+ subject { described_class.new attrs }
43
+
44
+ it { subject.title.must_equal 'the title' }
45
+ it { subject.body.must_equal 'the body' }
46
+ it { subject.action.must_equal 'submit' }
47
+ it { subject.url_args.must_equal ['a'] }
48
+ it { subject.device_token.must_equal '1234a' }
49
+ end
50
+
51
+ describe 'payload and co' do
52
+ subject { described_class.new attrs }
53
+
54
+ let(:parsed_payload) { JSON.parse(subject.to_json) }
55
+ let(:expected_payload) do
56
+ {
57
+ "aps" => {
58
+ "alert" => {
59
+ "title" => 'the title',
60
+ "body" => 'the body',
61
+ "action" => 'submit'
62
+ },
63
+ "url-args" => ['a']
64
+ }
65
+ }
66
+ end
67
+
68
+ it { parsed_payload.must_equal expected_payload }
69
+ it { subject.json_size.must_equal 92 }
70
+
71
+ describe 'no url_args' do
72
+ before do
73
+ subject.url_args = []
74
+ end
75
+
76
+ it { parsed_payload['aps'].has_key?('url-args').must_be_false }
77
+ end
78
+
79
+ describe 'no action' do
80
+ before do
81
+ subject.action = nil
82
+ end
83
+
84
+ it { parsed_payload['aps']['alert'].has_key?('action').must_be_false }
85
+ end
86
+ end
87
+ end
88
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crusade-apns
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Rouchy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-09-17 00:00:00.000000000 Z
11
+ date: 2013-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip
@@ -111,12 +111,17 @@ files:
111
111
  - lib/crusade/apns.rb
112
112
  - lib/crusade/apns/configuration.rb
113
113
  - lib/crusade/apns/crypto/user_token_generator.rb
114
+ - lib/crusade/apns/notification.rb
115
+ - lib/crusade/apns/notification_builder.rb
116
+ - lib/crusade/apns/notification_encoder.rb
117
+ - lib/crusade/apns/push_notification.rb
114
118
  - lib/crusade/apns/push_package/directory_structure_generator.rb
115
119
  - lib/crusade/apns/push_package/manifest_generator.rb
116
120
  - lib/crusade/apns/push_package/signature_generator.rb
117
121
  - lib/crusade/apns/push_package/website_file_generator.rb
118
122
  - lib/crusade/apns/push_package/zip_file_generator.rb
119
123
  - lib/crusade/apns/push_package_generator.rb
124
+ - lib/crusade/apns/socket_connection.rb
120
125
  - lib/crusade/apns/version.rb
121
126
  - signature
122
127
  - test/fixtures/config.yml
@@ -136,19 +141,23 @@ files:
136
141
  - test/fixtures/ssl/manifest_example.json
137
142
  - test/fixtures/ssl/signature
138
143
  - test/fixtures/ssl/signature_example
144
+ - test/integration/push_notification_test.rb
139
145
  - test/integration/push_package_generator_test.rb
140
146
  - test/integration/user_token_generator_test.rb
147
+ - test/support/fake_apns_server.rb
141
148
  - test/support/fixtures.rb
142
149
  - test/support/ssl_support.rb
143
150
  - test/support/zip_support.rb
144
151
  - test/test_helper.rb
145
152
  - test/unit/configuration_test.rb
153
+ - test/unit/notification_builder_test.rb
154
+ - test/unit/notification_encoder_test.rb
155
+ - test/unit/notification_test.rb
146
156
  - test/unit/push_package/directory_structure_generator_test.rb
147
157
  - test/unit/push_package/manifest_generator_test.rb
148
158
  - test/unit/push_package/signature_generator_test.rb
149
159
  - test/unit/push_package/website_file_generator_test.rb
150
160
  - test/unit/push_package/zip_file_generator_test.rb
151
- - test/unit/push_package_generator_test.rb
152
161
  homepage: ''
153
162
  licenses:
154
163
  - MIT
@@ -191,16 +200,20 @@ test_files:
191
200
  - test/fixtures/ssl/manifest_example.json
192
201
  - test/fixtures/ssl/signature
193
202
  - test/fixtures/ssl/signature_example
203
+ - test/integration/push_notification_test.rb
194
204
  - test/integration/push_package_generator_test.rb
195
205
  - test/integration/user_token_generator_test.rb
206
+ - test/support/fake_apns_server.rb
196
207
  - test/support/fixtures.rb
197
208
  - test/support/ssl_support.rb
198
209
  - test/support/zip_support.rb
199
210
  - test/test_helper.rb
200
211
  - test/unit/configuration_test.rb
212
+ - test/unit/notification_builder_test.rb
213
+ - test/unit/notification_encoder_test.rb
214
+ - test/unit/notification_test.rb
201
215
  - test/unit/push_package/directory_structure_generator_test.rb
202
216
  - test/unit/push_package/manifest_generator_test.rb
203
217
  - test/unit/push_package/signature_generator_test.rb
204
218
  - test/unit/push_package/website_file_generator_test.rb
205
219
  - test/unit/push_package/zip_file_generator_test.rb
206
- - test/unit/push_package_generator_test.rb
@@ -1,6 +0,0 @@
1
- require File.expand_path '../../test_helper.rb', __FILE__
2
-
3
- require 'crusade/apns/push_package_generator'
4
-
5
- describe Crusade::APNS::PushPackageGenerator do
6
- end