hashblue-api 0.0.8 → 0.1.1
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.
- data/Rakefile +1 -1
- data/hashblue-api.gemspec +2 -2
- data/lib/hashblue/api.rb +4 -16
- data/lib/hashblue/api/error.rb +68 -30
- data/lib/hashblue/api/message.rb +13 -1
- data/lib/hashblue/api/model.rb +6 -2
- data/lib/hashblue/api/request.rb +5 -1
- data/lib/hashblue/api/subscriber.rb +19 -0
- data/lib/hashblue/api/test_helper.rb +32 -17
- data/test/helpers/test_helper_test.rb +35 -2
- metadata +4 -4
data/Rakefile
CHANGED
data/hashblue-api.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{hashblue-api}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.1.1"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["FreeRange"]
|
9
|
-
s.date = %q{2010-05-
|
9
|
+
s.date = %q{2010-05-19}
|
10
10
|
s.email = %q{lets@gofreerange.com}
|
11
11
|
s.extra_rdoc_files = [
|
12
12
|
"README"
|
data/lib/hashblue/api.rb
CHANGED
@@ -58,22 +58,10 @@ module Hashblue
|
|
58
58
|
autoload :Subscriber, 'hashblue/api/subscriber'
|
59
59
|
autoload :Error, 'hashblue/api/error'
|
60
60
|
autoload :TestHelper, 'hashblue/api/test_helper'
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
# Raised when the API responded in a way that we couldn't handle.
|
66
|
-
class BadResponseError < Hashblue::API::Error; end
|
67
|
-
# Raised when the object requested cannot be found.
|
68
|
-
class NotFoundError < Hashblue::API::Error; end
|
69
|
-
# Raised when the object requested is not available given the
|
70
|
-
# credentials used; most often is is because a Contact or Message
|
71
|
-
# belongs to a different Subscriber
|
72
|
-
class AccessDeniedError < Hashblue::API::Error
|
73
|
-
def status
|
74
|
-
:unauthorized
|
75
|
-
end
|
76
|
-
end
|
61
|
+
autoload :NotRespondingError, 'hashblue/api/error'
|
62
|
+
autoload :BadResponseError, 'hashblue/api/error'
|
63
|
+
autoload :NotFoundError, 'hashblue/api/error'
|
64
|
+
autoload :AccessDeniedError, 'hashblue/api/error'
|
77
65
|
|
78
66
|
# Sets the timeout used before raising an error.
|
79
67
|
def self.timeout(value)
|
data/lib/hashblue/api/error.rb
CHANGED
@@ -1,39 +1,77 @@
|
|
1
1
|
# The base Hashblue Error class, which will be sent serialized
|
2
2
|
# as JSON from the API itself.
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
else
|
10
|
-
Hashblue::API::BadResponseError.new(response)
|
11
|
-
end
|
12
|
-
end
|
3
|
+
module Hashblue
|
4
|
+
module API
|
5
|
+
class Error < StandardError
|
6
|
+
def self.status(return_status)
|
7
|
+
define_method(:status) { return_status }
|
8
|
+
end
|
13
9
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
10
|
+
def self.from_response(response)
|
11
|
+
if response.json? && response.keys == ["error"]
|
12
|
+
response["error"].keys.first.constantize.new(response["error"].values.first)
|
13
|
+
elsif response.code == 401
|
14
|
+
Hashblue::API::AccessDeniedError.new(response)
|
15
|
+
else
|
16
|
+
Hashblue::API::BadResponseError.new(response)
|
17
|
+
end
|
18
|
+
end
|
22
19
|
|
23
|
-
|
24
|
-
|
25
|
-
|
20
|
+
def initialize(response)
|
21
|
+
if response.respond_to?(:body)
|
22
|
+
@body = response.body
|
23
|
+
@headers = response.headers
|
24
|
+
else
|
25
|
+
@response = response
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def status
|
30
|
+
:not_found
|
31
|
+
end
|
26
32
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
+
def to_s
|
34
|
+
str = "<##{self.class.name}:#{self.object_id}"
|
35
|
+
if @body && @headers
|
36
|
+
str + "@headers=#{@headers.inspect} @body=#{@body.inspect}>"
|
37
|
+
else
|
38
|
+
str + "@response=#{@response.inspect}>"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_json
|
43
|
+
{self.class.name => self.message}
|
44
|
+
end
|
33
45
|
end
|
34
|
-
end
|
35
46
|
|
36
|
-
|
37
|
-
|
47
|
+
# Raised when the API fails to respond with a timeout. The timeout
|
48
|
+
# can be set using Hashblue::API.timeout
|
49
|
+
class NotRespondingError < StandardError; end
|
50
|
+
# Raised when the API responded in a way that we couldn't handle.
|
51
|
+
class BadResponseError < Hashblue::API::Error; end
|
52
|
+
# Raised when the object requested cannot be found.
|
53
|
+
class NotFoundError < Hashblue::API::Error; end
|
54
|
+
# Raised when the object requested is not available given the
|
55
|
+
# credentials used; most often is is because a Contact or Message
|
56
|
+
# belongs to a different Subscriber
|
57
|
+
class AccessDeniedError < Hashblue::API::Error
|
58
|
+
status :unauthorized
|
59
|
+
end
|
60
|
+
# Raised when a subscriber has reached the limit of messages they
|
61
|
+
# can send.
|
62
|
+
class RateLimitError < Hashblue::API::Error
|
63
|
+
status :service_unavailable
|
64
|
+
def message
|
65
|
+
@response
|
66
|
+
end
|
67
|
+
end
|
68
|
+
# Raised when the request was malformed (e.g. bad parameters)
|
69
|
+
class BadRequestError < Hashblue::API::Error
|
70
|
+
status :bad_request
|
71
|
+
end
|
72
|
+
# Raised when the API is unable to fulfil a request
|
73
|
+
class ServiceUnavailableError < Hashblue::API::Error
|
74
|
+
status :service_unavailable
|
75
|
+
end
|
38
76
|
end
|
39
77
|
end
|
data/lib/hashblue/api/message.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'hashblue/api'
|
2
2
|
|
3
3
|
# A Message represents an SMS either sent or received by this Subscriber.
|
4
|
-
# It should not be instantiated directly; Messages can be loaded either
|
4
|
+
# It should not be instantiated directly; Messages can be loaded either
|
5
5
|
# using Subscriber#messages, or Contact#messages.
|
6
6
|
class Hashblue::API::Message < Hashblue::API::Model
|
7
7
|
# Deletes a specific message. Note that you must be authenticated as the
|
@@ -11,6 +11,18 @@ class Hashblue::API::Message < Hashblue::API::Model
|
|
11
11
|
Hashblue::API.delete("/subscribers/#{subscriber_id}/messages/#{id}.json")
|
12
12
|
end
|
13
13
|
|
14
|
+
# Favourites a specific message. You must be authenticated as the Subscriber
|
15
|
+
# in order to favourite a message; otherwise an AccessDeniedError will be
|
16
|
+
# raised.
|
17
|
+
def self.favourite(subscriber_id, id)
|
18
|
+
Hashblue::API.post("/subscribers/#{subscriber_id}/favourites.json", :query => {:id => id})
|
19
|
+
end
|
20
|
+
|
21
|
+
# Un Favourite a specific message. Once authenticated this will mark a message as not a favourite
|
22
|
+
def self.unfavourite(subscriber_id, id)
|
23
|
+
Hashblue::API.delete("/subscribers/#{subscriber_id}/favourites/#{id}.json")
|
24
|
+
end
|
25
|
+
|
14
26
|
#:nodoc:
|
15
27
|
def initialize(attributes = {})
|
16
28
|
super
|
data/lib/hashblue/api/model.rb
CHANGED
@@ -18,11 +18,11 @@ class Hashblue::API::Model #:nodoc:all
|
|
18
18
|
@partial_path = "#{@collection}/#{@element}".freeze
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def self.model_name
|
23
23
|
@_model_name ||= Name.new(self.name.from(15))
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def self.from_json(models)
|
27
27
|
models.map do |attributes|
|
28
28
|
new(attributes)
|
@@ -52,4 +52,8 @@ class Hashblue::API::Model #:nodoc:all
|
|
52
52
|
def get(path, query_options={})
|
53
53
|
Hashblue::API.get(path, :query => query_options)
|
54
54
|
end
|
55
|
+
|
56
|
+
def post(path, query_options = {})
|
57
|
+
Hashblue::API.post(path, :query => query_options)
|
58
|
+
end
|
55
59
|
end
|
data/lib/hashblue/api/request.rb
CHANGED
@@ -7,6 +7,10 @@ module Hashblue::API::Request #:nodoc:all
|
|
7
7
|
_request { super }
|
8
8
|
end
|
9
9
|
|
10
|
+
def post(path, options={})
|
11
|
+
_request { super }
|
12
|
+
end
|
13
|
+
|
10
14
|
def _request
|
11
15
|
_with_timeout do
|
12
16
|
response = Response.new(yield)
|
@@ -26,7 +30,7 @@ module Hashblue::API::Request #:nodoc:all
|
|
26
30
|
|
27
31
|
class Response < DelegateClass(HTTParty::Response)
|
28
32
|
def success?
|
29
|
-
code == 200
|
33
|
+
code == 200 || code == 201
|
30
34
|
end
|
31
35
|
|
32
36
|
def json?
|
@@ -47,6 +47,15 @@ class Hashblue::API::Subscriber < Hashblue::API::Model
|
|
47
47
|
Hashblue::API::Message.from_json(get("#{_path}/messages.json", options))
|
48
48
|
end
|
49
49
|
|
50
|
+
# Returns an Array of Message objects for the subscriber that are marked as favourites
|
51
|
+
#
|
52
|
+
# All messages that are marked as favourites are included in the returned Array.
|
53
|
+
#
|
54
|
+
# See above for possible options this method takes.
|
55
|
+
def favourites(options={})
|
56
|
+
Hashblue::API::Message.from_json(get("#{_path}/favourites.json", options))
|
57
|
+
end
|
58
|
+
|
50
59
|
# Returns an Array of Message objects for the Subscriber.
|
51
60
|
#
|
52
61
|
# All messages that have been deleted for all contacts are included in
|
@@ -57,6 +66,16 @@ class Hashblue::API::Subscriber < Hashblue::API::Model
|
|
57
66
|
Hashblue::API::Message.from_json(get("#{_path}/messages/deleted.json", options))
|
58
67
|
end
|
59
68
|
|
69
|
+
# Send a message from the Subscriber to the given number.
|
70
|
+
#
|
71
|
+
# The sent message will be returned if it was successfully sent. The contact_msisdn
|
72
|
+
# should be in the form "447#########".
|
73
|
+
def send_message(contact_msisdn, content)
|
74
|
+
result = post("#{_path}/messages.json", :message => {:content => content,
|
75
|
+
:contact_msisdn => contact_msisdn} )
|
76
|
+
Hashblue::API::Message.new(result)
|
77
|
+
end
|
78
|
+
|
60
79
|
private
|
61
80
|
|
62
81
|
def _path
|
@@ -24,31 +24,35 @@ require 'json'
|
|
24
24
|
# The test helper will ensure that the data you are passing to +stub_hashblue_api+
|
25
25
|
# matches the data that will be returned to your application by the API.
|
26
26
|
module Hashblue::API::TestHelper
|
27
|
-
API_KEYS = %w(contact_msisdn content timestamp id sent).map(&:to_sym)
|
27
|
+
API_KEYS = %w(contact_msisdn content timestamp id sent favourite).map(&:to_sym)
|
28
28
|
|
29
29
|
def stub_hashblue_api(subscriber_id, options={})
|
30
|
-
options.reverse_merge!(:contacts => [], :messages => [])
|
30
|
+
options.reverse_merge!(:contacts => [], :messages => [], :favourites => [])
|
31
31
|
|
32
|
-
messages = options[:messages].map do |attributes|
|
33
|
-
|
34
|
-
:subscriber_id => subscriber_id, :sent => false,
|
35
|
-
:timestamp => Time.zone.now.to_json}.merge(prepare_and_check_attributes(attributes))
|
32
|
+
options[:messages] = options[:messages].map do |attributes|
|
33
|
+
build_hashblue_api_message(subscriber_id, attributes)
|
36
34
|
end
|
37
35
|
|
38
|
-
|
36
|
+
favourites = []
|
37
|
+
options[:messages].each do |message|
|
39
38
|
Hashblue::API.stubs(:delete).with("/subscribers/#{subscriber_id}/messages/#{message[:id]}.json")
|
39
|
+
Hashblue::API.stubs(:post).with("/subscribers/#{subscriber_id}/favourites.json",
|
40
|
+
:query => {:id => message[:id]}).returns(message)
|
41
|
+
if message[:favourite] == true
|
42
|
+
Hashblue::API.stubs(:delete).with("/subscribers/#{subscriber_id}/favourites/#{message[:id]}.json")
|
43
|
+
favourites << message
|
44
|
+
end
|
40
45
|
end
|
41
46
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
else
|
48
|
-
Hashblue::API.stubs(:get).with("/subscribers/#{subscriber_id}/messages.json", anything).returns(messages)
|
49
|
-
end
|
47
|
+
Hashblue::API.stubs(:post).with("/subscribers/#{subscriber_id}/messages.json", anything).returns(
|
48
|
+
{:content => "sent message", :to=>"123", :sent => true}
|
49
|
+
)
|
50
|
+
|
51
|
+
Hashblue::API.stubs(:get).with("/subscribers/#{subscriber_id}/favourites.json", anything).returns(favourites)
|
50
52
|
|
51
|
-
|
53
|
+
Hashblue::API.stubs(:get).with("/subscribers/#{subscriber_id}/messages.json", anything).returns(options[:messages])
|
54
|
+
|
55
|
+
contacts = options[:messages].group_by { |m| m[:contact_msisdn] }.map do |msisdn, contact_messages|
|
52
56
|
explicit_contact = options[:contacts].find { |c| c[:msisdn] == msisdn }
|
53
57
|
id = explicit_contact ? explicit_contact[:id] : __next_firehose_id
|
54
58
|
|
@@ -59,7 +63,7 @@ module Hashblue::API::TestHelper
|
|
59
63
|
end
|
60
64
|
|
61
65
|
contacts_with_no_messages = options[:contacts].reject do |contact|
|
62
|
-
messages.find { |m| m[:contact_msisdn] == contact[:msisdn] }
|
66
|
+
options[:messages].find { |m| m[:contact_msisdn] == contact[:msisdn] }
|
63
67
|
end.map do |contact|
|
64
68
|
{:latest_message => nil, :id => __next_firehose_id}.merge(contact)
|
65
69
|
end
|
@@ -69,8 +73,19 @@ module Hashblue::API::TestHelper
|
|
69
73
|
Hashblue::API.stubs(:get).with("/subscribers/#{subscriber_id}/contacts.json", anything).returns(contacts)
|
70
74
|
end
|
71
75
|
|
76
|
+
def stub_hashblue_api_message_search(subscriber_id, query, search_results)
|
77
|
+
Hashblue::API.stubs(:get).with("/subscribers/#{subscriber_id}/messages.json",
|
78
|
+
{:query => {:q => query}}).returns(search_results.map { |m| build_hashblue_api_message(subscriber_id, m) })
|
79
|
+
end
|
80
|
+
|
72
81
|
private
|
73
82
|
|
83
|
+
def build_hashblue_api_message(subscriber_id, attributes = {})
|
84
|
+
{:id => __next_firehose_id, :content => "Hello", :contact_msisdn => "123",
|
85
|
+
:subscriber_id => subscriber_id, :sent => false, :favourite => false,
|
86
|
+
:timestamp => Time.zone.now.to_json}.merge(prepare_and_check_attributes(attributes))
|
87
|
+
end
|
88
|
+
|
74
89
|
def prepare_and_check_attributes(message)
|
75
90
|
unless (message.keys - API_KEYS).empty?
|
76
91
|
raise "#{message.keys.inspect} doesn't match the current API (#{API_KEYS.inspect})"
|
@@ -40,9 +40,42 @@ class TestHelperTest < Test::Unit::TestCase
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
should "stub sending a message" do
|
44
|
+
stub_hashblue_api(@subscriber.id)
|
45
|
+
|
46
|
+
message = @subscriber.send_message("123", "Hey man post this shizzle")
|
47
|
+
assert_equal true, message.sent
|
48
|
+
end
|
49
|
+
|
50
|
+
should "stub favourite messages for a subscriber" do
|
51
|
+
messages = [{:content => "Blah", :contact_msisdn => "123", :timestamp => 1.day.ago, :favourite => true}]
|
52
|
+
stub_hashblue_api(@subscriber.id, :messages => messages)
|
53
|
+
|
54
|
+
messages = @subscriber.favourites
|
55
|
+
assert_equal 1, messages.length
|
56
|
+
assert_equal "Blah", messages.first.content
|
57
|
+
end
|
58
|
+
|
59
|
+
should "stub marking a message as a favourite" do
|
60
|
+
messages = [{:id=>"123"}]
|
61
|
+
stub_hashblue_api(@subscriber.id, :messages => messages)
|
62
|
+
|
63
|
+
assert_nothing_raised do
|
64
|
+
Hashblue::API::Message.favourite(@subscriber.id, "123")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
should "stub marking a message as unfavourite when stubbing favourites" do
|
69
|
+
messages = [{:content => "Blah", :contact_msisdn => "123", :timestamp => 1.day.ago, :id=>"123", :favourite => true}]
|
70
|
+
stub_hashblue_api(@subscriber.id, :messages => messages)
|
71
|
+
|
72
|
+
assert_nothing_raised do
|
73
|
+
Hashblue::API::Message.unfavourite(@subscriber.id, "123")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
43
77
|
should "stub querying messages by keyword" do
|
44
|
-
|
45
|
-
{:id => "456", :content => "sure thing we'll do that"}], :q => "office")
|
78
|
+
stub_hashblue_api_message_search(@subscriber.id, "office", [{:id => "123", :content => "I'll meet you at the office"}])
|
46
79
|
messages = @subscriber.messages(:q => "office")
|
47
80
|
assert_equal 1, messages.length
|
48
81
|
assert_equal "I'll meet you at the office", messages.first.content
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 1
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- FreeRange
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-24 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|