Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Skeleton, started working on specs for it.
- Loading branch information
0 parents
commit 68276ed
Showing
12 changed files
with
313 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/spec/certificates/us.pem | ||
Gemfile.lock | ||
.test-env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--colour | ||
--format=documentation |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
source "https://rubygems.org" | ||
|
||
gemspec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
require 'rubygems' | ||
require 'rake' | ||
require 'rspec/core' | ||
require 'rspec/core/rake_task' | ||
require 'bundler/gem_tasks' | ||
|
||
desc "Run all specs in spec directory (excluding plugin specs)" | ||
RSpec::Core::RakeTask.new(:spec) | ||
|
||
task :default => :spec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
require 'net/http' | ||
require 'xml/libxml/xmlrpc' | ||
|
||
class Pyapns2 | ||
|
||
require 'pyapns2/version' | ||
|
||
class Error < StandardError; end | ||
|
||
attr_reader :host, :port | ||
|
||
class ProvisionedClient | ||
|
||
attr_reader :client, :app_id | ||
|
||
def initialize(client, app_id) | ||
@client = client | ||
@app_id = app_id | ||
end | ||
|
||
# See Pyapns2::Client#notify, with the exception this version prefills in the app_id. | ||
def notify(token, notification = nil) | ||
client.notify app_id, token, notification | ||
end | ||
|
||
# See Pyapns2::Client#feedback, with the exception this version prefills in the app_id. | ||
def feedback | ||
client.feedback app_id | ||
end | ||
|
||
def inspect | ||
"#<#{self.class.name} server=#{host}:#{port}, app_id=#{app_id}>" | ||
end | ||
|
||
end | ||
|
||
# Returns a pre-provisioned client that also automatically prepends | ||
# the app_id automatically to all api calls, making giving a simpler interface. | ||
def self.provision(options = {}) | ||
host, port = options.delete(:host), options.delete(:port) | ||
host ||= "localhost" | ||
port ||= 7077 | ||
client = new(host, port) | ||
client.provision(options) | ||
ProvisionedClient.new client, options[:app_id] | ||
end | ||
|
||
def initialize(host = 'localhost', port = 7077) | ||
raise ArgumentError, "host must be a string" unless host.is_a?(String) | ||
raise ArgumentError, "port must be a number" unless port.is_a?(Numeric) | ||
@host = host | ||
@port = port | ||
@http = Net::HTTP.new host, port | ||
@xmlrpc = XML::XMLRPC::Client.new @http, "/" | ||
end | ||
|
||
def inspect | ||
"#<#{self.class.name} server=#{host}:#{port}>" | ||
end | ||
|
||
# Given a hash of options, calls provision on the pyapns server. This | ||
# expects the following options and will raise an ArgumentError if they're | ||
# not given: | ||
# | ||
# :app_id - A String name for your application | ||
# :timeout - A number (e.g. 15) for how long to time out after when connecting to the apn server | ||
# :env / :environment - One of production or sandbox. The type of server to connect to. | ||
# :cert - Either a path to the certificate file or the certificate contents as a string. | ||
def provision(options) | ||
options[:environment] = options.delete(:env) if options.has_key?(:env) | ||
app_id = options[:app_id] | ||
timeout = options[:timeout] | ||
cert = options[:cert] | ||
env = options[:environment] | ||
raise ArgumentError, ":app_id must be a string" unless app_id.is_a?(String) | ||
raise ArgumentError, ":cert must be a string" unless cert.is_a?(String) | ||
raise ArgumentError, ":environment (or :env) must be one of sandbox or production" unless %w(production sandbox).include?(env) | ||
raise ArgumentError, ":timeout must be a valid integer" unless timeout.is_a?(Numeric) && timeout >= 0 | ||
@xmlrpc.call 'provision', app_id, cert, env, timeout | ||
true | ||
rescue LibXML::XML::XMLRPC::RemoteCallError => e | ||
raise Error.new e.message | ||
end | ||
|
||
# The main notification endpoint. Takes the app_id as the first argument, and then one | ||
# of three sets of notification data: | ||
# | ||
# 1. A single token (as a string) and notification (as a dictionary) | ||
# 2. A hash of token to notifications. | ||
# 3. An array of tokens mapping to an array of notifications. | ||
# | ||
# Under the hook, it will automatically convert it to the most appropriate form before continuing. | ||
# Will raise ArgumentError if you attempt to pass in bad information. | ||
def notify(app_id, token, notification = nil) | ||
if token.is_a?(Hash) | ||
token, notification = extra_notification_info_from_hash token | ||
end | ||
raise ArgumentError, "Please ensure you provide an app_id" unless app_id | ||
raise ArgumentError, "Please ensure you provide a single notification or an array of notifications" unless typed_item_of(notification, Hash) | ||
raise ArgumentError, "Please ensure you provide device tokens or a string of tokens" unless typed_item_of(token, String) | ||
types = [notification.is_a?(Array), token.is_a?(Array)] | ||
if types.any? && !types.all? | ||
raise ArgumentError, "The notifications and the strings must both be arrays if one is." | ||
end | ||
@xmlrpc.call 'notify', app_id, token, notification | ||
true | ||
rescue LibXML::XML::XMLRPC::RemoteCallError => e | ||
raise Error.new e.message | ||
end | ||
|
||
# Takes an app id and returns the list of feedback from pyapns. | ||
def feedback(app_id) | ||
@xmlrpc.call('feedback', app_id).params | ||
rescue LibXML::XML::XMLRPC::RemoteCallError => e | ||
raise Error.new e.message | ||
end | ||
|
||
private | ||
|
||
def extra_notification_info_from_hash(hash) | ||
tokens, notifications = [], [] | ||
hash.each_pair do |k,v| | ||
tokens << k | ||
notifications << v | ||
end | ||
return tokens, notifications | ||
end | ||
|
||
def typed_item_of(value, klass) | ||
value.is_a?(klass) || (value.is_a?(Array) && value.all? { |v| v.is_a?(klass) }) | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
class Pyapns2 | ||
VERSION = "1.0.0" | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# -*- encoding: utf-8 -*- | ||
lib = File.expand_path('../lib/', __FILE__) | ||
$:.unshift lib unless $:.include?(lib) | ||
|
||
require 'pyapns2/version' | ||
|
||
Gem::Specification.new do |s| | ||
s.name = "pyapns2" | ||
s.version = Pyapns2::VERSION | ||
s.platform = Gem::Platform::RUBY | ||
s.authors = ["Darcy Laycock"] | ||
s.email = ["darcy@filtersquad.com"] | ||
s.homepage = "http://github.com/filtersquad" | ||
s.summary = "An alternative ruby client for the pyapns push notification server with an emphasis on Ruby 1.9 support." | ||
s.description = "Pyapns2 provides an alterantive, simpler client for the pyapns push notification server, using libxml-xmlrpc to handle all of the xmlrpc.\nIt also is tested against Ruby 1.9" | ||
s.required_rubygems_version = ">= 1.3.6" | ||
|
||
s.add_dependency 'libxml-xmlrpc' | ||
s.add_development_dependency 'rake' | ||
s.add_development_dependency 'rspec', '~> 2.4' | ||
s.add_development_dependency 'rr', '~> 1.0' | ||
s.add_development_dependency 'webmock' | ||
s.add_development_dependency 'vcr' | ||
|
||
s.files = Dir.glob("{lib}/**/*") | ||
s.require_path = 'lib' | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
This is a fake certificate. |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
require 'spec_helper' | ||
|
||
describe Pyapns2 do | ||
|
||
let(:config) { TestConfiguration.instance } | ||
|
||
let(:options) { config.provisioning_options } | ||
let(:client) { Pyapns2.new config.host, config.port } | ||
|
||
describe 'provisioning' do | ||
|
||
it 'should raise an argument error with an invalid app_id' do | ||
expect { client.provision options.merge(app_id: nil) }.to raise_error ArgumentError | ||
expect { client.provision options.merge(app_id: "") }.to raise_error ArgumentError | ||
expect { client.provision options.merge(app_id: 123) }.to raise_error ArgumentError | ||
end | ||
|
||
it 'should raise an argument error with an invalid cert' | ||
|
||
it 'should raise an argument error with an invalid timeout' | ||
|
||
it 'should raise an argument error with an invalid environment' | ||
|
||
context 'hitting the server' do | ||
|
||
it 'should return true' | ||
|
||
end | ||
|
||
end | ||
|
||
describe 'notifying a user' do | ||
|
||
it 'should raise an error with an invalid app_id' | ||
|
||
it 'should raise an error with invalid tokens' | ||
|
||
it 'should raise an error with invalid notifications' | ||
|
||
it 'should raise an error with mismatched types' | ||
|
||
context 'hitting the server' do | ||
|
||
it 'should raise an exception if notifications fail' | ||
|
||
it 'should return true if the notification works' | ||
|
||
it 'should work with a hash' | ||
|
||
it 'should work with a single notification' | ||
|
||
it 'should work with an array' | ||
|
||
it 'should return the error with an unknown app_id' | ||
|
||
end | ||
|
||
end | ||
|
||
describe 'getting feedback' do | ||
|
||
it 'should raise an error with an invalid app_id' | ||
|
||
context 'htting the server' do | ||
|
||
it 'should correctly return an array' | ||
|
||
it 'should return the error with an invalid app id' | ||
|
||
end | ||
|
||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#!/usr/bin/env bash | ||
twistd --syslog -n web --class=pyapns.server.APNSServer --port=7077 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
$LOAD_PATH.unshift Pathname(__FILE__).dirname.dirname.join("lib").to_s | ||
|
||
require 'webmock' | ||
require 'vcr' | ||
require 'rr' | ||
|
||
require 'pyapns2' | ||
|
||
require 'singleton' | ||
class TestConfiguration | ||
|
||
include Singleton | ||
|
||
attr_reader :host, :port, :token, :cert_path, :fake_app | ||
|
||
def initialize | ||
fake_token = (1..64).map { |t| rand(16).to_s(16) }.join | ||
self.host = (ENV['PYAPNS_HOST'] || 'localhost') | ||
self.port = (ENV['PYAPNS_PORT'] || 7077).to_i | ||
self.token = (ENV['TEST_PUSH_TOKEN'] || fake_token) | ||
self.cert_path = File.expand_path "../certificates/#{(ENV['PYAPNS_CERT'] || "fake")}.pem", __FILE__ | ||
self.fake_app = "fake-app-#{Time.now.to_i}" | ||
end | ||
|
||
def cert | ||
@cert ||= File.read(cert_path) | ||
end | ||
|
||
def provisioning_options | ||
{ | ||
app_id: app_id, | ||
cert: cert, | ||
environment: 'sandbox', | ||
timeout: 15 | ||
} | ||
end | ||
|
||
end | ||
|
||
p $pyapns_default_options | ||
|
||
VCR.configure do |c| | ||
c.cassette_library_dir = 'spec/cassettes' | ||
c.hook_into :webmock | ||
c.default_cassette_options = {:record => :new_episodes} | ||
# Filter out parts of the test | ||
config = TestConfiguration.instance | ||
%w(pyapns_host pyapns_port notification_token pyapns_fake_app).each do |var_name| | ||
c.filter_sensitive_data("{#{var_name}}") { config.send var_name } | ||
end | ||
end | ||
|
||
RSpec.configure do |config| | ||
config.mock_with :rr | ||
end |