xednese 0.0.1 → 0.1.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -1
  3. data/README.md +22 -3
  4. data/Rakefile +8 -2
  5. data/bin/xednese +16 -0
  6. data/lib/xednese.rb +36 -9
  7. data/lib/xednese/account.rb +37 -0
  8. data/lib/xednese/account/dispatcher.rb +30 -0
  9. data/lib/xednese/account/messages.rb +47 -0
  10. data/lib/xednese/accounts.rb +14 -6
  11. data/lib/xednese/batch.rb +68 -0
  12. data/lib/xednese/batches.rb +33 -0
  13. data/lib/xednese/client.rb +46 -0
  14. data/lib/xednese/messages.rb +24 -0
  15. data/lib/xednese/requests/account.rb +17 -0
  16. data/lib/xednese/requests/batch.rb +17 -0
  17. data/lib/xednese/requests/messages.rb +1 -1
  18. data/lib/xednese/responses/batch.rb +20 -0
  19. data/lib/xednese/responses/batches.rb +15 -0
  20. data/lib/xednese/responses/status.rb +22 -0
  21. data/lib/xednese/users.rb +3 -8
  22. data/lib/xednese/version.rb +1 -1
  23. data/scenarios/accounts/getting_all_accounts.rb +37 -0
  24. data/scenarios/accounts/getting_an_account.rb +29 -0
  25. data/scenarios/helper.rb +113 -0
  26. data/spec/helper.rb +3 -3
  27. data/spec/xednese/{dispatcher_spec.rb → account/dispatcher_spec.rb} +5 -4
  28. data/spec/xednese/account/messages_spec.rb +56 -0
  29. data/spec/xednese/account_spec.rb +93 -0
  30. data/spec/xednese/accounts_spec.rb +19 -14
  31. data/spec/xednese/batch_spec.rb +121 -0
  32. data/spec/xednese/batches_spec.rb +70 -0
  33. data/spec/xednese/client_spec.rb +76 -2
  34. data/spec/xednese/messages_spec.rb +25 -1
  35. data/spec/xednese/requests/account_spec.rb +20 -0
  36. data/spec/xednese/requests/batch_spec.rb +20 -0
  37. data/spec/xednese/requests/messages_spec.rb +1 -1
  38. data/spec/xednese/responses/account_spec.rb +1 -1
  39. data/spec/xednese/responses/accounts_spec.rb +1 -1
  40. data/spec/xednese/responses/batch_spec.rb +83 -0
  41. data/spec/xednese/responses/batches_spec.rb +95 -0
  42. data/spec/xednese/responses/message_dispatcher_headers_spec.rb +1 -1
  43. data/spec/xednese/responses/message_header_spec.rb +1 -1
  44. data/spec/xednese/responses/message_headers_spec.rb +1 -1
  45. data/spec/xednese/responses/status_spec.rb +55 -0
  46. data/spec/xednese/users_spec.rb +1 -12
  47. data/spec/xednese_spec.rb +52 -12
  48. metadata +58 -8
  49. data/lib/xednese/dispatcher.rb +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 381d98d8d9e52c8f4ad3136d50780e79eca43d75
4
- data.tar.gz: a3e8b964f56061bafe0ce6bc8401f92d3f557f58
3
+ metadata.gz: 847dade493b0ea06b341f02631f96df1fb5b9ba8
4
+ data.tar.gz: b5993ebab4f9baf48c75d71bf78599d8ad65dc27
5
5
  SHA512:
6
- metadata.gz: efb4d2396b366aa322791529f947d6b2409ea1c98323b8cbdb138dadc368f962c0b029a871e572ef6540d387a245891c771ddc833c842887f69c6876be73650f
7
- data.tar.gz: 87d1652e6ed3033da2e2ba1df223d3e82ed52b7259d3fefa73a97b98ca4bf0625ffa03e7a18eef58174452df8e989f5f7138be7caa24adc158bea47d4866d1df
6
+ metadata.gz: 3b92a2320ed4c2fe49c93df492ecdbb00b72fc483c82a6eaf2d6b569a1fbf43e6bd659443510d62ed18c29492b6c99bd8e390a86a98ec4da1caa8125d0cd8395
7
+ data.tar.gz: 5cb5ade70cee76dcb0d52074f75d52607490fb4b6ce4519972d790c1622d6a6677c0d2ab2c16c52defe5a29261323c7aad67cee9cf5abc4b96d3d593aaa07f72
data/Gemfile CHANGED
@@ -1,9 +1,10 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'seq', '~> 0.2.0'
4
- gem 'serialisable', '~> 0.0.0'
4
+ gem 'serialisable', '~> 0.1.0'
5
5
  gem 'nokogiri', '~> 1.6'
6
6
 
7
7
  gem 'rake', '~> 10.1'
8
8
  gem 'minitest', '~> 5.2'
9
9
  gem 'mocha', '~> 1.0'
10
+ gem 'webmock', '~> 1.17'
data/README.md CHANGED
@@ -3,11 +3,30 @@
3
3
  ``` ruby
4
4
  require 'xednese'
5
5
 
6
- esendex = Esendex.new('me@company.com', 'password', 'account reference')
7
- p esendex.messages.sent.first.body
6
+ esendex = Esendex.new('me@company.com', 'password')
7
+ p esendex.messages.sent.first.summary
8
8
  #=> "..."
9
9
 
10
- response = esendex.dispatcher.send('Hey guys', '445275XXX')
10
+ account = esendex.account("EX0000")
11
+ response = account.dispatcher.send('Hey guys', '445275XXX')
11
12
  p response.message_headers.map(&:id)
12
13
  #=> ["..."]
13
14
  ```
15
+
16
+ Xednese is a client library for interacting with the
17
+ [Esendex REST API][exapi]. It is currently under active development and should
18
+ not be considered stable enough for general use.
19
+
20
+
21
+ ## Running locally
22
+
23
+ Clone the repo, then to get the run the tests:
24
+
25
+ ``` bash
26
+ $ gen install bundler
27
+ $ bundle install
28
+ $ bundle exec rake
29
+ ...
30
+ ```
31
+
32
+ [exapi]: developers.esendex.com/APIs/REST-API/
data/Rakefile CHANGED
@@ -1,9 +1,15 @@
1
1
  require 'rake/testtask'
2
2
 
3
- Rake::TestTask.new(:test) do |t|
3
+ Rake::TestTask.new(:spec) do |t|
4
4
  t.libs << 'lib' << 'spec'
5
5
  t.pattern = 'spec/**/*_spec.rb'
6
6
  t.verbose = true
7
7
  end
8
8
 
9
- task :default => :test
9
+ Rake::TestTask.new(:acceptance) do |t|
10
+ t.libs << 'lib' << 'scenarios'
11
+ t.pattern = 'scenarios/*/*.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ task :default => [:spec, :acceptance]
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'xednese'
4
+ require 'pry'
5
+
6
+ if ARGV.size != 2
7
+ abort "Usage: xednese <username> <password>"
8
+ end
9
+
10
+ esendex = Esendex.new(ARGV.first, ARGV.last)
11
+
12
+ puts <<EOS
13
+ xednese: Call methods on 'esendex'; CTRL-D to exit.
14
+ EOS
15
+
16
+ binding.pry quiet: true, prompt: Pry::SIMPLE_PROMPT
@@ -1,3 +1,4 @@
1
+ require 'forwardable'
1
2
  require 'net/http'
2
3
  require 'nokogiri'
3
4
  require 'seq/paged'
@@ -7,39 +8,65 @@ require 'time'
7
8
  require_relative 'xednese/version'
8
9
  require_relative 'xednese/client'
9
10
 
11
+ require_relative 'xednese/requests/account'
12
+ require_relative 'xednese/requests/batch'
10
13
  require_relative 'xednese/requests/messages'
11
14
 
12
15
  require_relative 'xednese/responses/parser'
13
16
  require_relative 'xednese/responses/account'
14
17
  require_relative 'xednese/responses/accounts'
18
+ require_relative 'xednese/responses/status'
19
+ require_relative 'xednese/responses/batch'
20
+ require_relative 'xednese/responses/batches'
15
21
  require_relative 'xednese/responses/message_dispatcher_headers'
16
22
  require_relative 'xednese/responses/message_header'
17
23
  require_relative 'xednese/responses/message_headers'
18
24
 
25
+ require_relative 'xednese/account'
19
26
  require_relative 'xednese/accounts'
20
- require_relative 'xednese/dispatcher'
27
+ require_relative 'xednese/batch'
28
+ require_relative 'xednese/batches'
21
29
  require_relative 'xednese/messages'
22
30
  require_relative 'xednese/users'
23
31
 
32
+ require_relative 'xednese/account/dispatcher'
33
+ require_relative 'xednese/account/messages'
34
+
24
35
  class Esendex
25
- Credentials = Struct.new(:username, :password, :account_reference)
36
+ Credentials = Struct.new(:username, :password)
26
37
 
27
- def initialize(username, password, account_reference)
28
- @credentials = Credentials.new(username, password, account_reference)
38
+ # @param username [String]
39
+ # @param password [String]
40
+ def initialize(username, password)
41
+ @credentials = Credentials.new(username, password)
29
42
  end
30
43
 
31
- def account
32
- Accounts.new(@credentials)
44
+ # @param reference [String]
45
+ # @return [Account]
46
+ # @see http://developers.esendex.com/APIs/REST-API/accounts
47
+ def account(reference)
48
+ accounts.find {|a| a.reference == reference }
33
49
  end
34
50
 
35
- def dispatcher
36
- Dispatcher.new(@credentials)
51
+ # @return [Accounts]
52
+ # @see http://developers.esendex.com/APIs/REST-API/accounts
53
+ def accounts
54
+ Accounts.new(@credentials)
37
55
  end
38
56
 
39
- def user
57
+ # @return [Users]
58
+ def users
40
59
  Users.new(@credentials)
41
60
  end
42
61
 
62
+ # @return [Batches]
63
+ # @see http://developers.esendex.com/APIs/REST-API/messagebatches
64
+ def batches
65
+ Batches.new(@credentials)
66
+ end
67
+
68
+ # @return [Messages]
69
+ # @see http://developers.esendex.com/APIs/REST-API/messageheaders
43
70
  def messages
44
71
  Messages.new(@credentials)
45
72
  end
@@ -0,0 +1,37 @@
1
+ class Esendex
2
+
3
+ class Account
4
+ def initialize(credentials, response)
5
+ @credentials = credentials
6
+ @response = response
7
+ end
8
+
9
+ # @return [Dispatcher]
10
+ # @see http://developers.esendex.com/APIs/REST-API/messagedispatcher
11
+ def dispatcher
12
+ Dispatcher.new(@credentials, reference)
13
+ end
14
+
15
+ # @return [Messages]
16
+ def messages
17
+ Messages.new(@credentials, reference)
18
+ end
19
+
20
+ extend Forwardable
21
+
22
+ def_delegators :@response, :id, :reference, :address, :type,
23
+ :messages_remaining, :expires_on, :role
24
+
25
+ def label
26
+ @label || @response.label
27
+ end
28
+
29
+ def label=(value)
30
+ request = Requests::Account.new(label: value)
31
+
32
+ Client.put(@credentials, "v1.0/accounts/#{id}", request) do |code, body|
33
+ @label = value
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,30 @@
1
+ class Esendex
2
+ class Account
3
+ class Dispatcher
4
+
5
+ # @see Esendex::Account#dispatcher
6
+ # @api private
7
+ def initialize(credentials, reference)
8
+ @credentials = credentials
9
+ @reference = reference
10
+ end
11
+
12
+ # Sends a message to a single recipient.
13
+ # @param body [String] the message body
14
+ # @param to [String] the number to send to
15
+ # @return [Responses::MessageDispatcherHeaders]
16
+ def send(body, to)
17
+ args = {
18
+ account_reference: @reference,
19
+ messages: [{to: to, body: body}]
20
+ }
21
+
22
+ messages = Requests::Messages.new(args)
23
+
24
+ Client.post(@credentials, 'v1.0/messagedispatcher', messages) do |code, body|
25
+ Responses::MessageDispatcherHeaders.deserialise(body)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,47 @@
1
+ class Esendex
2
+ class Account
3
+ class Messages
4
+ PAGE_COUNT = 25
5
+
6
+ # @see Esendex::Account#messages
7
+ # @api private
8
+ def initialize(credentials, reference)
9
+ @credentials = credentials
10
+ @reference = reference
11
+ end
12
+
13
+ # @return [Enumerable<Responses::MessageHeader>] an Enumerable that
14
+ # iterates over all sent messages. Requests are made for fixed size
15
+ # pages when required.
16
+ def sent
17
+ Seq::Paged.new do |page|
18
+ params = {
19
+ startIndex: PAGE_COUNT * page,
20
+ count: PAGE_COUNT,
21
+ accountReference: @reference
22
+ }
23
+
24
+ Client.get(@credentials, 'v1.0/messageheaders', params) do |status, data|
25
+ Responses::MessageHeaders.deserialise(data).message_headers
26
+ end
27
+ end
28
+ end
29
+
30
+ # @return [Enumerable<Responses::MessageHeader>] an Enumerable that
31
+ # iterates over all received messages. Requests are made for fixed size
32
+ # pages when required.
33
+ def received
34
+ Seq::Paged.new do |page|
35
+ params = {
36
+ startIndex: PAGE_COUNT * page,
37
+ count: PAGE_COUNT
38
+ }
39
+
40
+ Client.get(@credentials, "v1.0/inbox/#{@reference}/messages", params) do |status, data|
41
+ Responses::MessageHeaders.deserialise(data).message_headers
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,24 +1,32 @@
1
1
  class Esendex
2
+
3
+ # Provides methods for working with Accounts. An Accounts instance is an
4
+ # Enumerable, and will only make requests when needed.
2
5
  class Accounts
6
+
7
+ # @see Esendex#accounts
8
+ # @api private
3
9
  def initialize(credentials)
4
10
  @credentials = credentials
5
11
  end
6
12
 
7
- def reference
8
- @credentials.account_reference
9
- end
10
-
13
+ # @yield [Account] Calls the provided block with each account the user has
14
+ # access to
11
15
  def each(&block)
12
16
  Client.get(@credentials, 'v1.0/accounts') {|status, data|
13
- Responses::Accounts.deserialise(data).accounts
17
+ Responses::Accounts.deserialise(data).accounts.map do |account|
18
+ Account.new(@credentials, account)
19
+ end
14
20
  }.each(&block)
15
21
  end
16
22
 
17
23
  include Enumerable
18
24
 
25
+ # @param id [String] the id of the account to return
26
+ # @return [Account] Returns the account with the given id.
19
27
  def get(id)
20
28
  Client.get(@credentials, "v1.0/accounts/#{id}") do |status, data|
21
- Responses::Account.deserialise(data)
29
+ Account.new @credentials, Responses::Account.deserialise(data)
22
30
  end
23
31
  end
24
32
  end
@@ -0,0 +1,68 @@
1
+ class Esendex
2
+ class Batch
3
+ PAGE_COUNT = 25
4
+
5
+ # @see Esendex#batches
6
+ # @api private
7
+ def initialize(credentials, response)
8
+ @credentials = credentials
9
+ @response = response
10
+ end
11
+
12
+ extend Forwardable
13
+
14
+ def_delegators :@response, :id, :created_at, :batch_size,
15
+ :persisted_batch_size, :account_reference, :created_by
16
+
17
+ def status
18
+ s = @response.status
19
+
20
+ [:acknowledged,
21
+ :authorisation_failed,
22
+ :connecting,
23
+ :delivered,
24
+ :failed,
25
+ :partially_delivered,
26
+ :rejected,
27
+ :scheduled,
28
+ :sent,
29
+ :submitted,
30
+ :validity_period_expired,
31
+ :cancelled].find do |type|
32
+ s.send(type) == 1
33
+ end
34
+ end
35
+
36
+ def name
37
+ @name || @response.name
38
+ end
39
+
40
+ def name=(value)
41
+ request = Requests::Batch.new(name: value)
42
+
43
+ Client.put(@credentials, "v1.1/messagebatches/#{id}", request) do |status, data|
44
+ @name = value
45
+ end
46
+ end
47
+
48
+ def cancel!
49
+ Client.delete(@credentials, "v1.1/messagebatches/#{id}/schedule")
50
+ end
51
+
52
+ # @return [Enumerable<Responses::MessageHeader>] an Enumerable that iterates
53
+ # over all messages in the batch. Requests are made for fixed size pages
54
+ # when required.
55
+ def messages
56
+ Seq::Paged.new do |page|
57
+ params = {
58
+ startIndex: PAGE_COUNT * page,
59
+ count: PAGE_COUNT
60
+ }
61
+
62
+ Client.get(@credentials, "v1.0/messagebatches/#{id}/messages", params) do |status, data|
63
+ Responses::MessageHeaders.deserialise(data).message_headers
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,33 @@
1
+ class Esendex
2
+
3
+ # Provides methods for working with message batches. A Batches instance is
4
+ # Enumerable, and will only make requests when needed.
5
+ class Batches
6
+
7
+ # @see Esendex#batches
8
+ # @api private
9
+ def initialize(credentials)
10
+ @credentials = credentials
11
+ end
12
+
13
+ # @yield [Batch] Calls the provided block with each batch the user has
14
+ # access to
15
+ def each(&block)
16
+ Client.get(@credentials, 'v1.1/messagebatches') {|status, data|
17
+ Responses::Batches.deserialise(data).batches.map do |batch|
18
+ Batch.new(@credentials, batch)
19
+ end
20
+ }.each(&block)
21
+ end
22
+
23
+ include Enumerable
24
+
25
+ # @param id [String] the id of the batch to return
26
+ # @return [Batch] Returns the batch with the given id.
27
+ def get(id)
28
+ Client.get(@credentials, "v1.1/messagebatches/#{id}") do |status, data|
29
+ Batch.new @credentials, Responses::Batch.deserialise(data)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,9 +1,18 @@
1
1
  class Esendex
2
+
3
+ # Client is a wrapper for Net::HTTP for calling through to the Esendex REST
4
+ # API.
2
5
  class Client
3
6
  ROOT = URI('https://api.esendex.com/')
4
7
  USER_AGENT = "ruby/xednese-#{VERSION}"
5
8
  CONTENT_TYPE = "application/xml"
6
9
 
10
+ # @param credentials [Credentials]
11
+ # @param path [String] Path to GET (after the api.esendex.com/ part)
12
+ # @param args [Hash] An optional hash of query parameters to be appended to
13
+ # the url.
14
+ #
15
+ # @return [Integer, String] Returns the status code and the response body.
7
16
  def self.get(credentials, path, args={})
8
17
  uri = url(path)
9
18
  uri.query = URI.encode_www_form(args)
@@ -14,6 +23,26 @@ class Esendex
14
23
  block_given? ? yield(code, body) : [code, body]
15
24
  end
16
25
 
26
+ # @param credentials [Credentials]
27
+ # @param path [String] Path to DELETE
28
+ #
29
+ # @return [Integer, String] Returns the status code and the response body.
30
+ def self.delete(credentials, path)
31
+ uri = url(path)
32
+ request = Net::HTTP::Delete.new(uri)
33
+
34
+ code, body = execute(credentials, request)
35
+
36
+ block_given? ? yield(code, body) : [code, body]
37
+ end
38
+
39
+ # @param credentials [Credentials]
40
+ # @param path [String] Path to POST to
41
+ # @param object [#serialise] The object that represents the content to be
42
+ # posted. This must be an object with a method #serialise that returns a
43
+ # string of xml.
44
+ #
45
+ # @return [Integer, String] Returns the status code and the response body.
17
46
  def self.post(credentials, path, object)
18
47
  request = Net::HTTP::Post.new(url(path))
19
48
  request.body = object.serialise
@@ -24,6 +53,23 @@ class Esendex
24
53
  block_given? ? yield(code, body) : [code, body]
25
54
  end
26
55
 
56
+ # @param credentials [Credentials]
57
+ # @param path [String] Path to PUT to
58
+ # @param object [#serialise] The object that represents the content to be
59
+ # posted. This must be an object with a method #serialise that returns a
60
+ # string of xml.
61
+ #
62
+ # @return [Integer, String] Returns the status code and the response body.
63
+ def self.put(credentials, path, object)
64
+ request = Net::HTTP::Put.new(url(path))
65
+ request.body = object.serialise
66
+ request.content_type = CONTENT_TYPE
67
+
68
+ code, body = execute(credentials, request)
69
+
70
+ block_given? ? yield(code, body) : [code, body]
71
+ end
72
+
27
73
  private
28
74
 
29
75
  def self.url(path)