pushr-wns 1.0.0.pre

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.
Files changed (29) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +35 -0
  4. data/lib/pushr-wns/version.rb +3 -0
  5. data/lib/pushr/configuration_wns.rb +15 -0
  6. data/lib/pushr/daemon/wns.rb +26 -0
  7. data/lib/pushr/daemon/wns_support/access_token.rb +57 -0
  8. data/lib/pushr/daemon/wns_support/connection_manager.rb +34 -0
  9. data/lib/pushr/daemon/wns_support/connection_wns.rb +110 -0
  10. data/lib/pushr/feedback_wns.rb +10 -0
  11. data/lib/pushr/message_wns.rb +36 -0
  12. data/lib/pushr/message_wns_toast.rb +9 -0
  13. data/lib/pushr/wns.rb +15 -0
  14. data/spec/lib/pushr/configuration_wns_spec.rb +35 -0
  15. data/spec/lib/pushr/daemon/wns_spec.rb +13 -0
  16. data/spec/lib/pushr/daemon/wns_support/access_token_spec.rb +27 -0
  17. data/spec/lib/pushr/daemon/wns_support/connection_manager_spec.rb +56 -0
  18. data/spec/lib/pushr/daemon/wns_support/connection_wns_spec.rb +38 -0
  19. data/spec/lib/pushr/feedback_wns_spec.rb +27 -0
  20. data/spec/lib/pushr/message_wns_spec.rb +49 -0
  21. data/spec/lib/pushr/message_wns_toast_spec.rb +38 -0
  22. data/spec/spec_helper.rb +37 -0
  23. data/spec/support/vcr.rb +4 -0
  24. data/spec/vcr/pushr/daemon/wns_support/access_token/does_not_receive_token_unsuccesful.yml +46 -0
  25. data/spec/vcr/pushr/daemon/wns_support/access_token/receives_token_succesful.yml +46 -0
  26. data/spec/vcr/pushr/daemon/wns_support/connection_manager/creates_first_connection_succesful.yml +47 -0
  27. data/spec/vcr/pushr/daemon/wns_support/connection_manager/creates_multiple_connections_succesful.yml +99 -0
  28. data/spec/vcr/pushr/daemon/wns_support/connection_wns/sends_a_message_succesful.yml +51 -0
  29. metadata +268 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: db74413af288d7801a8b7bfa71fce8339c75feafb6b9452fd248247fed978113
4
+ data.tar.gz: 0a7d25172fae765322497e8d2725f650b778e3caf99e582d85b614d5720853cb
5
+ SHA512:
6
+ metadata.gz: 118acb36e76025bd83ccd244862972aa5a37bc3dddcf16727ac4a983edfeed0886c9af63755d2a297e6e5a288e88059f27b81e46d3107a3aafe460d34f5b902d
7
+ data.tar.gz: b673fb65bc2617c2079fec5a99a79b7575b928435201fa7cf89120dbb80f7b4379f787792bb9e0880e81468d700391a58e6268221654215bed0056908429bfbc
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,35 @@
1
+ # PushrWns
2
+
3
+ [![Build Status](https://travis-ci.org/9to5/pushr-wns.svg?branch=master)](https://travis-ci.org/9to5/pushr-wns)
4
+ [![Code Climate](https://codeclimate.com/github/9to5/pushr-wns.png)](https://codeclimate.com/github/9to5/pushr-wns)
5
+ [![Coverage Status](https://coveralls.io/repos/9to5/pushr-wns/badge.png)](https://coveralls.io/r/9to5/pushr-wns)
6
+
7
+ Please see [pushr-core](https://github.com/9to5/pushr-core) for more information.
8
+
9
+ ## Configuration
10
+
11
+ Redis:
12
+ ```ruby
13
+ Pushr::ConfigurationWns.create(app: 'app_name', connections: 2, enabled: true, client_id: '<client_id>', client_secret: '<client_secret>)
14
+ ```
15
+
16
+ YAML:
17
+ ```yaml
18
+ - type: Pushr::ConfigurationWns
19
+ app: wns-app
20
+ enabled: true
21
+ connections: 1
22
+ client_id: client_id_here
23
+ client_secret: client_secret_here
24
+ ```
25
+
26
+ ## Sending notifications
27
+
28
+ WNS:
29
+ ```ruby
30
+ Pushr::MessageWns.create(
31
+ app: 'test', channel_uri: 'https://db3.notify.windows.com/?token=token',
32
+ data: '<toast launch=""><visual lang="en-US"><binding template="ToastImageAndText01"><image id="1" '\
33
+ 'src="World" /><text id="1">Hello</text></binding></visual></toast>',
34
+ content_type: 'text/xml', x_wns_type: 'wns/toast')
35
+ ```
@@ -0,0 +1,3 @@
1
+ module PushrWns
2
+ VERSION = '1.0.0.pre'
3
+ end
@@ -0,0 +1,15 @@
1
+ module Pushr
2
+ class ConfigurationWns < Pushr::Configuration
3
+ attr_accessor :client_id, :client_secret
4
+ validates :client_id, :client_secret, presence: true
5
+
6
+ def name
7
+ :wns
8
+ end
9
+
10
+ def to_hash
11
+ { type: self.class.to_s, app: app, enabled: enabled, connections: connections, client_id: client_id,
12
+ client_secret: client_secret }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ module Pushr
2
+ module Daemon
3
+ class Wns
4
+ attr_accessor :configuration, :handlers
5
+
6
+ def initialize(options)
7
+ @configuration = options
8
+ @handlers = []
9
+ end
10
+
11
+ def start
12
+ access_token = Pushr::Daemon::WnsSupport::AccessToken.new(configuration)
13
+ configuration.connections.times do |i|
14
+ connection = WnsSupport::ConnectionManager.new(configuration.name, access_token, i + 1)
15
+ handler = MessageHandler.new("pushr:#{configuration.key}", connection, configuration.app, i + 1)
16
+ handler.start
17
+ @handlers << handler
18
+ end
19
+ end
20
+
21
+ def stop
22
+ @handlers.map(&:stop)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,57 @@
1
+ # In the authentication with WNS, the cloud service submits an HTTP request over Secure Sockets Layer (SSL). The
2
+ # parameters are supplied in the "application/x-www-for-urlencoded" format. Supply your Package SID in the "client_id"
3
+ # field and your secret key in the "client_secret" field. For syntax details, see the access token request reference.
4
+
5
+ module Pushr
6
+ module Daemon
7
+ module WnsSupport
8
+ class AuthenticationError < StandardError; end
9
+
10
+ class AccessToken
11
+ def initialize(configuration)
12
+ @configuration = configuration
13
+ @expires_in = Time.now
14
+ @semaphore = Mutex.new
15
+ end
16
+
17
+ def get
18
+ @semaphore.synchronize { request_token if expired? || @access_token.nil? }
19
+ @access_token
20
+ end
21
+
22
+ def expired?
23
+ Time.now > @expires_in
24
+ end
25
+
26
+ def request_token
27
+ uri = URI('https://login.live.com/accesstoken.srf')
28
+ request = Net::HTTP::Post.new(uri.request_uri)
29
+ request.set_form_data(params)
30
+ response = http(uri).request(request)
31
+ response_hsh = MultiJson.load(response.body)
32
+ if response.code.to_i != 200
33
+ fail AuthenticationError, response_hsh['error_description']
34
+ else
35
+ process_response(response_hsh)
36
+ end
37
+ end
38
+
39
+ def http(uri)
40
+ http = Net::HTTP.new(uri.host, uri.port)
41
+ http.use_ssl = true
42
+ http
43
+ end
44
+
45
+ def process_response(params)
46
+ @expires_in = Time.now + params['expires_in']
47
+ @access_token = params['access_token']
48
+ end
49
+
50
+ def params
51
+ { grant_type: 'client_credentials', scope: 'notify.windows.com', client_id: @configuration.client_id,
52
+ client_secret: @configuration.client_secret }
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,34 @@
1
+ module Pushr
2
+ module Daemon
3
+ module WnsSupport
4
+ class ConnectionManager
5
+ attr_accessor :name
6
+ def initialize(app_name, access_token, i)
7
+ @pool = {}
8
+ @app_name = app_name
9
+ @access_token = access_token
10
+ @i = i
11
+ @name = "#{app_name}: ConnectionWns #{i}"
12
+ end
13
+
14
+ def get(uri)
15
+ key = "#{uri.scheme}://#{uri.host}"
16
+ unless @pool.key? key
17
+ @pool[key] = ConnectionWns.new(@app_name, @access_token, @i)
18
+ end
19
+ @pool[key]
20
+ end
21
+
22
+ def write(message)
23
+ uri = URI.parse(message.channel_uri)
24
+ connection = get(uri)
25
+ connection.write(message)
26
+ end
27
+
28
+ def size
29
+ @pool.size
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,110 @@
1
+ module Pushr
2
+ module Daemon
3
+ module WnsSupport
4
+ class ConnectionError < StandardError; end
5
+
6
+ class ConnectionWns
7
+ attr_reader :response, :name
8
+ IDLE_PERIOD = 5 * 60
9
+
10
+ def initialize(app_name, access_token, i)
11
+ @app_name = app_name
12
+ @access_token = access_token
13
+ @name = "#{app_name}: ConnectionWns #{i}"
14
+ end
15
+
16
+ def write(message)
17
+ retry_count = 0
18
+ begin
19
+ response = notification_request(message)
20
+ handle_error_response(response, message) unless response.code.eql? '200'
21
+ rescue => e
22
+ retry_count += 1
23
+ retry if retry_count < 10
24
+ raise e
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ # http://msdn.microsoft.com/en-us/library/windows/apps/hh465435.aspx
31
+ def handle_error_response(response, message)
32
+ case response.code.to_i
33
+ when 403
34
+ # TODO: retry with new credentials
35
+ when 404, 410
36
+ Pushr::FeedbackWns.create(app: @app_name, device: message.channel_uri, follow_up: 'delete',
37
+ failed_at: Time.now)
38
+ when 406
39
+ # TODO: Throttle
40
+ end
41
+
42
+ Pushr::Daemon.logger.error("[#{@name}] #{response.code} #{response.message}")
43
+ end
44
+
45
+ def notification_request(message)
46
+ uri = URI.parse(message.channel_uri)
47
+ connect(uri) unless @last_use
48
+ post(uri, message.data, headers(message))
49
+ end
50
+
51
+ def post(uri, data, headers, retry_count = 0)
52
+ reconnect_idle if idle_period_exceeded?
53
+
54
+ begin
55
+ response = @connection.post("#{uri.path}?#{uri.query}", data, headers)
56
+ @last_use = Time.now
57
+ rescue EOFError, Errno::ECONNRESET, Timeout::Error => e
58
+ retry_count += 1
59
+ Pushr::Daemon.logger.error("[#{@name}] Lost connection to #{PUSH_URL} (#{e.class.name}), reconnecting ##{retry_count}...")
60
+ if retry_count <= 3
61
+ reconnect
62
+ sleep 1
63
+ retry
64
+ end
65
+ raise ConnectionError, "#{@name} tried #{retry_count - 1} times to reconnect but failed (#{e.class.name})."
66
+ end
67
+
68
+ response
69
+ end
70
+
71
+ def open_http(host, port)
72
+ http = Net::HTTP.new(host, port)
73
+ http.use_ssl = true
74
+ http
75
+ end
76
+
77
+ def headers(message)
78
+ { 'Authorization' => "Bearer #{@access_token.get}",
79
+ 'Content-type' => message.content_type,
80
+ 'Content-length' => "#{message.data.bytes.count}",
81
+ 'X-WNS-RequestForStatus' => 'true',
82
+ 'X-WNS-Type' => message.x_wns_type }
83
+ end
84
+
85
+ def connect(uri)
86
+ @last_use = Time.now
87
+ @connection = open_http(uri.host, uri.port)
88
+ @connection.start
89
+ Pushr::Daemon.logger.info("[#{@name}] Connected to #{uri.scheme}://#{uri.host}")
90
+ end
91
+
92
+ def idle_period_exceeded?
93
+ # Timeout on the http connection is 5 minutes, reconnect after 5 minutes
94
+ @last_use + IDLE_PERIOD < Time.now
95
+ end
96
+
97
+ def reconnect_idle
98
+ Pushr::Daemon.logger.info("[#{@name}] Idle period exceeded, reconnecting...")
99
+ reconnect
100
+ end
101
+
102
+ def reconnect
103
+ @connection.finish
104
+ @last_use = Time.now
105
+ @connection.start
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,10 @@
1
+ module Pushr
2
+ class FeedbackWns < Pushr::Feedback
3
+ attr_accessor :device, :follow_up, :failed_at
4
+ validates :follow_up, inclusion: { in: %w(delete), message: '%{value} is not a valid follow-up' }
5
+
6
+ def to_hash
7
+ { type: 'Pushr::FeedbackWns', app: app, device: device, follow_up: follow_up, failed_at: failed_at }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,36 @@
1
+ module Pushr
2
+ class MessageWns < Pushr::Message
3
+ POSTFIX = 'wns'
4
+
5
+ attr_accessor :channel_uri, :data, :content_type, :x_wns_type
6
+ validates :channel_uri, :data, :content_type, :x_wns_type, presence: true
7
+ validates :content_type, inclusion: { in: ['text/xml', 'application/octet-stream'] }
8
+ validates :x_wns_type, inclusion: { in: ['wns/toast', 'wns/badge', 'wns/tile', 'wns/raw'] }
9
+ validate :channel_uri_domain
10
+ validate :data_size
11
+
12
+ def channel_uri_domain
13
+ return if channel_uri && URI.parse(channel_uri).host.end_with?('notify.windows.com')
14
+ errors.add(:channel_uri, 'channel_uri domain should end with \'notify.windows.com\'')
15
+ end
16
+
17
+ def data_size
18
+ errors.add(:data, 'is more thank 5000 bytes') if data && data.bytes.count > 5000
19
+ end
20
+
21
+ def to_message
22
+ hsh = {}
23
+ %w(channel_uri data).each do |variable|
24
+ hsh[variable] = send(variable) if send(variable)
25
+ end
26
+ MultiJson.dump(hsh)
27
+ end
28
+
29
+ def to_hash
30
+ hsh = { type: self.class.to_s, app: app, channel_uri: channel_uri, data: data, content_type: content_type,
31
+ x_wns_type: x_wns_type }
32
+ hsh[Pushr::Core.external_id_tag] = external_id if external_id
33
+ hsh
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,9 @@
1
+ module Pushr
2
+ class MessageWnsToast < Pushr::MessageWns
3
+ def initialize(attributes = {})
4
+ super(attributes)
5
+ self.content_type = 'text/xml'
6
+ self.x_wns_type = 'wns/toast'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'active_model'
4
+ require 'pushr-wns/version'
5
+ require 'pushr/configuration'
6
+ require 'pushr/configuration_wns'
7
+ require 'pushr/message'
8
+ require 'pushr/message_wns'
9
+ require 'pushr/message_wns_toast'
10
+ require 'pushr/feedback'
11
+ require 'pushr/feedback_wns'
12
+ require 'pushr/daemon/wns'
13
+ require 'pushr/daemon/wns_support/connection_manager'
14
+ require 'pushr/daemon/wns_support/connection_wns'
15
+ require 'pushr/daemon/wns_support/access_token'
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'pushr/configuration_wns'
3
+
4
+ describe Pushr::ConfigurationWns do
5
+
6
+ before(:each) do
7
+ Pushr::Core.configure do |config|
8
+ config.redis = ConnectionPool.new(size: 1, timeout: 1) { MockRedis.new }
9
+ end
10
+ end
11
+
12
+ describe 'all' do
13
+ it 'returns all configurations' do
14
+ expect(Pushr::Configuration.all).to eql([])
15
+ end
16
+ end
17
+
18
+ describe 'create' do
19
+ it 'should create a configuration' do
20
+ config = Pushr::ConfigurationWns.new(app: 'app_name', connections: 2, enabled: true)
21
+ expect(config.key).to eql('app_name:wns')
22
+ end
23
+ end
24
+
25
+ describe 'save' do
26
+ let(:config) do
27
+ Pushr::ConfigurationWns.new(app: 'app_name', connections: 2, enabled: true, client_id: 'id', client_secret: 'sec')
28
+ end
29
+ it 'should save a configuration' do
30
+ config.save
31
+ expect(Pushr::Configuration.all.count).to eql(1)
32
+ expect(Pushr::Configuration.all[0].class).to eql(Pushr::ConfigurationWns)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'pushr/daemon/wns'
3
+ require 'pushr/daemon/wns_support/connection_wns'
4
+
5
+ describe Pushr::Daemon::Wns do
6
+ let(:wns) { Pushr::Daemon::Wns.new(test: 'test') }
7
+
8
+ describe 'responds to' do
9
+ it 'configuration' do
10
+ expect(wns.configuration).to eql(test: 'test')
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'pushr/configuration_wns'
3
+ require 'pushr/daemon/wns_support/access_token'
4
+
5
+ describe Pushr::Daemon::WnsSupport::AccessToken do
6
+ subject { Pushr::Daemon::WnsSupport::AccessToken.new(config) }
7
+
8
+ let(:config) do
9
+ Pushr::ConfigurationWns.new(app: 'app_name', connections: 2, enabled: true, client_id: client_id,
10
+ client_secret: client_secret)
11
+ end
12
+ describe 'receives token' do
13
+ let(:client_id) { 'test' }
14
+ let(:client_secret) { 'secret' }
15
+ it 'succesful', :vcr do
16
+ expect(subject.get).to eql('access_token')
17
+ end
18
+ end
19
+
20
+ describe 'does not receive token' do
21
+ let(:client_id) { 'invalid_test' }
22
+ let(:client_secret) { 'invalid_secret' }
23
+ it 'unsuccesful', :vcr do
24
+ expect { subject.get }.to raise_error(Pushr::Daemon::WnsSupport::AuthenticationError)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+ require 'pushr/daemon'
3
+ require 'pushr/daemon/logger'
4
+ require 'pushr/configuration_wns'
5
+ require 'pushr/message_wns'
6
+ require 'pushr/daemon/wns_support/access_token'
7
+ require 'pushr/daemon/wns_support/connection_wns'
8
+ require 'pushr/daemon/wns_support/connection_manager'
9
+
10
+ describe Pushr::Daemon::WnsSupport::ConnectionManager do
11
+ before(:each) do
12
+ Pushr::Core.configure do |config|
13
+ config.redis = ConnectionPool.new(size: 1, timeout: 1) { MockRedis.new }
14
+ end
15
+
16
+ logger = double('logger')
17
+ allow(logger).to receive(:info)
18
+ allow(logger).to receive(:error)
19
+ allow(logger).to receive(:warn)
20
+ Pushr::Daemon.logger = logger
21
+ end
22
+
23
+ subject { Pushr::Daemon::WnsSupport::ConnectionManager.new('app_name', access_token, 1) }
24
+ let(:access_token) { double('Pushr::Daemon::WnsSupport::AccessToken', get: 'access_token') }
25
+
26
+ let(:message) do
27
+ hsh = { app: 'app_name', channel_uri: 'https://db3.notify.windows.com/?token=token',
28
+ data: '<toast launch=""><visual lang="en-US"><binding template="ToastImageAndText01"><image id="1" '\
29
+ 'src="World" /><text id="1">Hello</text></binding></visual></toast>',
30
+ content_type: 'text/xml', x_wns_type: 'wns/toast' }
31
+ Pushr::MessageWns.new(hsh)
32
+ end
33
+
34
+ let(:message2) do
35
+ hsh = { app: 'app_name', channel_uri: 'https://db4.notify.windows.com/?token=token',
36
+ data: '<toast launch=""><visual lang="en-US"><binding template="ToastImageAndText01"><image id="1" '\
37
+ 'src="World" /><text id="1">Hello</text></binding></visual></toast>',
38
+ content_type: 'text/xml', x_wns_type: 'wns/toast' }
39
+ Pushr::MessageWns.new(hsh)
40
+ end
41
+
42
+ describe 'creates first connection' do
43
+ it 'succesful', :vcr do
44
+ subject.write(message)
45
+ expect(subject.size).to eql(1)
46
+ end
47
+ end
48
+
49
+ describe 'creates multiple connections' do
50
+ it 'succesful', :vcr do
51
+ subject.write(message)
52
+ subject.write(message2)
53
+ expect(subject.size).to eql(2)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'pushr/daemon'
3
+ require 'pushr/wns'
4
+ require 'pushr/daemon/logger'
5
+ require 'pushr/message_wns'
6
+ require 'pushr/configuration_wns'
7
+ require 'pushr/daemon/delivery_error'
8
+
9
+ describe Pushr::Daemon::WnsSupport::ConnectionWns do
10
+ before(:each) do
11
+ Pushr::Core.configure do |config|
12
+ config.redis = ConnectionPool.new(size: 1, timeout: 1) { MockRedis.new }
13
+ end
14
+
15
+ logger = double('logger')
16
+ allow(logger).to receive(:info)
17
+ allow(logger).to receive(:error)
18
+ allow(logger).to receive(:warn)
19
+ Pushr::Daemon.logger = logger
20
+ end
21
+
22
+ let(:access_token) { double('Pushr::Daemon::WnsSupport::AccessToken', get: 'access_token') }
23
+ let(:message) do
24
+ hsh = { app: 'app_name', channel_uri: 'https://db3.notify.windows.com/?token=token',
25
+ data: '<toast launch=""><visual lang="en-US"><binding template="ToastImageAndText01"><image id="1" '\
26
+ 'src="World" /><text id="1">Hello</text></binding></visual></toast>' }
27
+ Pushr::MessageWnsToast.new(hsh)
28
+ end
29
+ let(:connection) { Pushr::Daemon::WnsSupport::ConnectionWns.new('app_name', access_token, 1) }
30
+
31
+ describe 'sends a message' do
32
+
33
+ it 'succesful', :vcr do
34
+ connection.write(message)
35
+ # TODO: expect(connection.write(message).code).to eql '200'
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'pushr/feedback_wns'
3
+
4
+ describe Pushr::FeedbackWns do
5
+
6
+ before(:each) do
7
+ Pushr::Core.configure do |config|
8
+ config.redis = ConnectionPool.new(size: 1, timeout: 1) { MockRedis.new }
9
+ end
10
+ end
11
+
12
+ describe 'create' do
13
+ it 'should create a feedback' do
14
+ feedback = Pushr::FeedbackWns.new(app: 'app_name', device: 'ab' * 20, follow_up: 'delete', failed_at: Time.now)
15
+ expect(feedback.app).to eql('app_name')
16
+ end
17
+ end
18
+
19
+ describe 'save' do
20
+ let!(:feedback) do
21
+ Pushr::FeedbackWns.create(app: 'app_name', device: 'ab' * 20, follow_up: 'delete', failed_at: Time.now)
22
+ end
23
+ it 'should save a feedback' do
24
+ expect(Pushr::Feedback.next.class).to eql(Pushr::FeedbackWns)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'pushr/message_wns'
3
+
4
+ describe Pushr::MessageWns do
5
+
6
+ before(:each) do
7
+ Pushr::Core.configure do |config|
8
+ config.redis = ConnectionPool.new(size: 1, timeout: 1) { MockRedis.new }
9
+ end
10
+ end
11
+
12
+ describe 'next' do
13
+ it 'returns next message' do
14
+ expect(Pushr::Message.next('pushr:app_name:wns')).to eql(nil)
15
+ end
16
+ end
17
+
18
+ let(:message) do
19
+ hsh = { app: 'app_name', channel_uri: channel_uri,
20
+ data: '<toast launch=""><visual lang="en-US"><binding template="ToastImageAndText01"><image id="1" '\
21
+ 'src="World" /><text id="1">Hello</text></binding></visual></toast>',
22
+ content_type: 'text/xml', x_wns_type: 'wns/toast' }
23
+ Pushr::MessageWns.new(hsh)
24
+ end
25
+ describe 'save' do
26
+ let(:channel_uri) { 'https://db3.notify.windows.com/?token=token' }
27
+
28
+ it 'should return true' do
29
+ expect(message.save).to eql true
30
+ end
31
+
32
+ it 'should save a message' do
33
+ message.save
34
+ expect(Pushr::Message.next('pushr:app_name:wns')).to be_kind_of(Pushr::MessageWns)
35
+ end
36
+
37
+ it 'should respond to to_message' do
38
+ expect(message.to_message).to be_kind_of(String)
39
+ end
40
+ end
41
+
42
+ describe 'validate channel_uri' do
43
+ let(:channel_uri) { 'https://db3.invalid.domain/?token=token' }
44
+ it 'should respond to to_message' do
45
+ expect(message.valid?).to be_falsy
46
+ expect(message.errors[:channel_uri]).to eql(['channel_uri domain should end with \'notify.windows.com\''])
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'pushr/message_wns'
3
+ require 'pushr/message_wns_toast'
4
+
5
+ describe Pushr::MessageWnsToast do
6
+
7
+ before(:each) do
8
+ Pushr::Core.configure do |config|
9
+ config.redis = ConnectionPool.new(size: 1, timeout: 1) { MockRedis.new }
10
+ end
11
+ end
12
+
13
+ describe 'next' do
14
+ it 'returns next message' do
15
+ expect(Pushr::Message.next('pushr:app_name:wns')).to eql(nil)
16
+ end
17
+ end
18
+
19
+ describe 'save' do
20
+ let(:message) do
21
+ hsh = { app: 'app_name', channel_uri: 'https://db3.notify.windows.com/?token=token-here', data: 'data' }
22
+ Pushr::MessageWnsToast.new(hsh)
23
+ end
24
+
25
+ it 'should return true' do
26
+ expect(message.save).to eql true
27
+ end
28
+
29
+ it 'should save a message' do
30
+ message.save
31
+ expect(Pushr::Message.next('pushr:app_name:wns')).to be_kind_of(Pushr::MessageWnsToast)
32
+ end
33
+
34
+ it 'should respond to to_message' do
35
+ expect(message.to_message).to be_kind_of(String)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,37 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+
6
+ require 'simplecov'
7
+ require 'coveralls'
8
+
9
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
10
+ SimpleCov::Formatter::HTMLFormatter,
11
+ Coveralls::SimpleCov::Formatter
12
+ ])
13
+ SimpleCov.start
14
+
15
+ require 'vcr'
16
+ require 'pushr/core'
17
+ require 'mock_redis'
18
+
19
+ Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
20
+
21
+ RSpec.configure do |config|
22
+ config.raise_errors_for_deprecations!
23
+ config.run_all_when_everything_filtered = true
24
+ # config.filter_run :focus
25
+
26
+ # Run specs in random order to surface order dependencies. If you find an
27
+ # order dependency and want to debug it, you can fix the order by providing
28
+ # the seed, which is printed after each run.
29
+ # --seed 1234
30
+ config.order = 'random'
31
+
32
+ config.around(:each, :vcr) do |example|
33
+ name = example.metadata[:full_description].split(/\s+/, 2).join('/').underscore.gsub(/[^\w\/]+/, '_')
34
+ options = example.metadata.slice(:record, :match_requests_on).except(:example_group)
35
+ VCR.use_cassette(name, options) { example.call }
36
+ end
37
+ end
@@ -0,0 +1,4 @@
1
+ VCR.configure do |c|
2
+ c.cassette_library_dir = File.join(Dir.pwd, 'spec', 'vcr')
3
+ c.hook_into :webmock
4
+ end
@@ -0,0 +1,46 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://login.live.com/accesstoken.srf
6
+ body:
7
+ encoding: US-ASCII
8
+ string: grant_type=client_credentials&scope=notify.windows.com&client_id=invalid_test&client_secret=invalid_secret
9
+ headers:
10
+ Accept-Encoding:
11
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12
+ Accept:
13
+ - "*/*"
14
+ User-Agent:
15
+ - Ruby
16
+ Content-Type:
17
+ - application/x-www-form-urlencoded
18
+ response:
19
+ status:
20
+ code: 400
21
+ message: Bad Request
22
+ headers:
23
+ Cache-Control:
24
+ - no-store
25
+ Pragma:
26
+ - no-cache
27
+ Content-Length:
28
+ - '66'
29
+ Content-Type:
30
+ - application/json
31
+ Server:
32
+ - Microsoft-IIS/7.5
33
+ X-Wlid-Error:
34
+ - '0x80049D59'
35
+ Ppserver:
36
+ - 'PPV: 30 H: BAYIDSLGN2E025 V: 0'
37
+ Date:
38
+ - Tue, 28 Oct 2014 13:26:38 GMT
39
+ Connection:
40
+ - close
41
+ body:
42
+ encoding: UTF-8
43
+ string: '{"error":"invalid_client","error_description":"Invalid client id"}'
44
+ http_version:
45
+ recorded_at: Tue, 28 Oct 2014 13:26:38 GMT
46
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,46 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://login.live.com/accesstoken.srf
6
+ body:
7
+ encoding: US-ASCII
8
+ string: grant_type=client_credentials&scope=notify.windows.com&client_id=test&client_secret=secret
9
+ headers:
10
+ Accept-Encoding:
11
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12
+ Accept:
13
+ - "*/*"
14
+ User-Agent:
15
+ - Ruby
16
+ Host:
17
+ - login.live.com
18
+ Content-Type:
19
+ - application/x-www-form-urlencoded
20
+ response:
21
+ status:
22
+ code: 200
23
+ message: OK
24
+ headers:
25
+ Cache-Control:
26
+ - no-store
27
+ Pragma:
28
+ - no-cache
29
+ Content-Length:
30
+ - '444'
31
+ Content-Type:
32
+ - application/json
33
+ Server:
34
+ - Microsoft-IIS/7.5
35
+ Ppserver:
36
+ - 'PPV: 30 H: BAYIDSLGN1A015 V: 0'
37
+ Date:
38
+ - Mon, 27 Oct 2014 16:48:34 GMT
39
+ Connection:
40
+ - close
41
+ body:
42
+ encoding: UTF-8
43
+ string: '{"token_type":"bearer","access_token":"access_token","expires_in":86400}'
44
+ http_version:
45
+ recorded_at: Mon, 27 Oct 2014 16:48:34 GMT
46
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,47 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://db3.notify.windows.com/?token=token
6
+ body:
7
+ encoding: UTF-8
8
+ string: <toast launch=""><visual lang="en-US"><binding template="ToastImageAndText01"><image
9
+ id="1" src="World" /><text id="1">Hello</text></binding></visual></toast>
10
+ headers:
11
+ Authorization:
12
+ - Bearer access_token
13
+ Content-Type:
14
+ - text/xml
15
+ Content-Length:
16
+ - '158'
17
+ X-Wns-Requestforstatus:
18
+ - 'true'
19
+ X-Wns-Type:
20
+ - wns/toast
21
+ Accept-Encoding:
22
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
23
+ Accept:
24
+ - "*/*"
25
+ User-Agent:
26
+ - Ruby
27
+ response:
28
+ status:
29
+ code: 401
30
+ message: Unauthorized
31
+ headers:
32
+ Content-Length:
33
+ - '0'
34
+ X-Wns-Msg-Id:
35
+ - 79647B0871EAD6E2
36
+ X-Wns-Debug-Trace:
37
+ - DB3WNS2011334
38
+ Www-Authenticate:
39
+ - Bearer error="invalid_request",error_description="Invalid token"
40
+ Date:
41
+ - Mon, 10 Nov 2014 10:21:43 GMT
42
+ body:
43
+ encoding: UTF-8
44
+ string: ''
45
+ http_version:
46
+ recorded_at: Mon, 10 Nov 2014 10:21:43 GMT
47
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,99 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://db3.notify.windows.com/?token=token
6
+ body:
7
+ encoding: UTF-8
8
+ string: <toast launch=""><visual lang="en-US"><binding template="ToastImageAndText01"><image
9
+ id="1" src="World" /><text id="1">Hello</text></binding></visual></toast>
10
+ headers:
11
+ Authorization:
12
+ - Bearer access_token
13
+ Content-Type:
14
+ - text/xml
15
+ Content-Length:
16
+ - '158'
17
+ X-Wns-Requestforstatus:
18
+ - 'true'
19
+ X-Wns-Type:
20
+ - wns/toast
21
+ Accept-Encoding:
22
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
23
+ Accept:
24
+ - "*/*"
25
+ User-Agent:
26
+ - Ruby
27
+ response:
28
+ status:
29
+ code: 200
30
+ message: OK
31
+ headers:
32
+ Content-Length:
33
+ - '0'
34
+ X-Wns-Deviceconnectionstatus:
35
+ - connected
36
+ X-Wns-Notificationstatus:
37
+ - received
38
+ X-Wns-Status:
39
+ - received
40
+ X-Wns-Msg-Id:
41
+ - 402E73F068974F89
42
+ X-Wns-Debug-Trace:
43
+ - DB3WNS4011733
44
+ Date:
45
+ - Mon, 27 Oct 2014 16:48:34 GMT
46
+ body:
47
+ encoding: UTF-8
48
+ string: ''
49
+ http_version:
50
+ recorded_at: Mon, 27 Oct 2014 16:48:35 GMT
51
+ - request:
52
+ method: post
53
+ uri: https://db4.notify.windows.com/?token=token
54
+ body:
55
+ encoding: UTF-8
56
+ string: <toast launch=""><visual lang="en-US"><binding template="ToastImageAndText01"><image
57
+ id="1" src="World" /><text id="1">Hello</text></binding></visual></toast>
58
+ headers:
59
+ Authorization:
60
+ - Bearer access_token
61
+ Content-Type:
62
+ - text/xml
63
+ Content-Length:
64
+ - '158'
65
+ X-Wns-Requestforstatus:
66
+ - 'true'
67
+ X-Wns-Type:
68
+ - wns/toast
69
+ Accept-Encoding:
70
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
71
+ Accept:
72
+ - "*/*"
73
+ User-Agent:
74
+ - Ruby
75
+ response:
76
+ status:
77
+ code: 200
78
+ message: OK
79
+ headers:
80
+ Content-Length:
81
+ - '0'
82
+ X-Wns-Deviceconnectionstatus:
83
+ - connected
84
+ X-Wns-Notificationstatus:
85
+ - received
86
+ X-Wns-Status:
87
+ - received
88
+ X-Wns-Msg-Id:
89
+ - 402E73F068974F89
90
+ X-Wns-Debug-Trace:
91
+ - DB3WNS4011733
92
+ Date:
93
+ - Mon, 27 Oct 2014 16:48:34 GMT
94
+ body:
95
+ encoding: UTF-8
96
+ string: ''
97
+ http_version:
98
+ recorded_at: Mon, 27 Oct 2014 16:48:35 GMT
99
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,51 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://db3.notify.windows.com/?token=token
6
+ body:
7
+ encoding: UTF-8
8
+ string: <toast launch=""><visual lang="en-US"><binding template="ToastImageAndText01"><image
9
+ id="1" src="World" /><text id="1">Hello</text></binding></visual></toast>
10
+ headers:
11
+ Authorization:
12
+ - Bearer access_token
13
+ Content-Type:
14
+ - text/xml
15
+ Content-Length:
16
+ - '158'
17
+ X-Wns-Requestforstatus:
18
+ - 'true'
19
+ X-Wns-Type:
20
+ - wns/toast
21
+ Accept-Encoding:
22
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
23
+ Accept:
24
+ - "*/*"
25
+ User-Agent:
26
+ - Ruby
27
+ response:
28
+ status:
29
+ code: 200
30
+ message: OK
31
+ headers:
32
+ Content-Length:
33
+ - '0'
34
+ X-Wns-Deviceconnectionstatus:
35
+ - connected
36
+ X-Wns-Notificationstatus:
37
+ - received
38
+ X-Wns-Status:
39
+ - received
40
+ X-Wns-Msg-Id:
41
+ - 402E73F068974F89
42
+ X-Wns-Debug-Trace:
43
+ - DB3WNS4011733
44
+ Date:
45
+ - Mon, 27 Oct 2014 16:48:34 GMT
46
+ body:
47
+ encoding: UTF-8
48
+ string: ''
49
+ http_version:
50
+ recorded_at: Mon, 27 Oct 2014 16:48:35 GMT
51
+ recorded_with: VCR 2.9.3
metadata ADDED
@@ -0,0 +1,268 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pushr-wns
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.pre
5
+ platform: ruby
6
+ authors:
7
+ - Tom Pesman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-10-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: multi_json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pushr-core
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activemodel
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: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: mock_redis
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
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'
125
+ - !ruby/object:Gem::Dependency
126
+ name: webmock
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: vcr
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rake
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: coveralls
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: rubocop
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ description: WNS support for the modular push daemon.
196
+ email:
197
+ - tom@tnux.net
198
+ executables: []
199
+ extensions: []
200
+ extra_rdoc_files: []
201
+ files:
202
+ - MIT-LICENSE
203
+ - README.md
204
+ - lib/pushr-wns/version.rb
205
+ - lib/pushr/configuration_wns.rb
206
+ - lib/pushr/daemon/wns.rb
207
+ - lib/pushr/daemon/wns_support/access_token.rb
208
+ - lib/pushr/daemon/wns_support/connection_manager.rb
209
+ - lib/pushr/daemon/wns_support/connection_wns.rb
210
+ - lib/pushr/feedback_wns.rb
211
+ - lib/pushr/message_wns.rb
212
+ - lib/pushr/message_wns_toast.rb
213
+ - lib/pushr/wns.rb
214
+ - spec/lib/pushr/configuration_wns_spec.rb
215
+ - spec/lib/pushr/daemon/wns_spec.rb
216
+ - spec/lib/pushr/daemon/wns_support/access_token_spec.rb
217
+ - spec/lib/pushr/daemon/wns_support/connection_manager_spec.rb
218
+ - spec/lib/pushr/daemon/wns_support/connection_wns_spec.rb
219
+ - spec/lib/pushr/feedback_wns_spec.rb
220
+ - spec/lib/pushr/message_wns_spec.rb
221
+ - spec/lib/pushr/message_wns_toast_spec.rb
222
+ - spec/spec_helper.rb
223
+ - spec/support/vcr.rb
224
+ - spec/vcr/pushr/daemon/wns_support/access_token/does_not_receive_token_unsuccesful.yml
225
+ - spec/vcr/pushr/daemon/wns_support/access_token/receives_token_succesful.yml
226
+ - spec/vcr/pushr/daemon/wns_support/connection_manager/creates_first_connection_succesful.yml
227
+ - spec/vcr/pushr/daemon/wns_support/connection_manager/creates_multiple_connections_succesful.yml
228
+ - spec/vcr/pushr/daemon/wns_support/connection_wns/sends_a_message_succesful.yml
229
+ homepage: https://github.com/9to5/pushr-wns
230
+ licenses:
231
+ - MIT
232
+ metadata: {}
233
+ post_install_message:
234
+ rdoc_options: []
235
+ require_paths:
236
+ - lib
237
+ required_ruby_version: !ruby/object:Gem::Requirement
238
+ requirements:
239
+ - - ">="
240
+ - !ruby/object:Gem::Version
241
+ version: 2.2.0
242
+ required_rubygems_version: !ruby/object:Gem::Requirement
243
+ requirements:
244
+ - - ">"
245
+ - !ruby/object:Gem::Version
246
+ version: 1.3.1
247
+ requirements: []
248
+ rubyforge_project:
249
+ rubygems_version: 2.7.6
250
+ signing_key:
251
+ specification_version: 4
252
+ summary: WNS (Windows Phone) part of the modular push daemon.
253
+ test_files:
254
+ - spec/lib/pushr/configuration_wns_spec.rb
255
+ - spec/lib/pushr/daemon/wns_spec.rb
256
+ - spec/lib/pushr/daemon/wns_support/access_token_spec.rb
257
+ - spec/lib/pushr/daemon/wns_support/connection_manager_spec.rb
258
+ - spec/lib/pushr/daemon/wns_support/connection_wns_spec.rb
259
+ - spec/lib/pushr/feedback_wns_spec.rb
260
+ - spec/lib/pushr/message_wns_spec.rb
261
+ - spec/lib/pushr/message_wns_toast_spec.rb
262
+ - spec/spec_helper.rb
263
+ - spec/support/vcr.rb
264
+ - spec/vcr/pushr/daemon/wns_support/access_token/does_not_receive_token_unsuccesful.yml
265
+ - spec/vcr/pushr/daemon/wns_support/access_token/receives_token_succesful.yml
266
+ - spec/vcr/pushr/daemon/wns_support/connection_manager/creates_first_connection_succesful.yml
267
+ - spec/vcr/pushr/daemon/wns_support/connection_manager/creates_multiple_connections_succesful.yml
268
+ - spec/vcr/pushr/daemon/wns_support/connection_wns/sends_a_message_succesful.yml