crusade-apns 0.5.0 → 0.6.0

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 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