tms_client 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/README.md +134 -0
- data/Rakefile +8 -0
- data/lib/tms_client/base.rb +28 -0
- data/lib/tms_client/client.rb +80 -0
- data/lib/tms_client/collection_resource.rb +53 -0
- data/lib/tms_client/connection.rb +30 -0
- data/lib/tms_client/instance_resource.rb +175 -0
- data/lib/tms_client/logger.rb +34 -0
- data/lib/tms_client/request.rb +17 -0
- data/lib/tms_client/resource/collections.rb +49 -0
- data/lib/tms_client/resource/command.rb +24 -0
- data/lib/tms_client/resource/command_type.rb +19 -0
- data/lib/tms_client/resource/commands.rb +3 -0
- data/lib/tms_client/resource/email.rb +9 -0
- data/lib/tms_client/resource/email_recipient.rb +7 -0
- data/lib/tms_client/resource/inbound_message.rb +7 -0
- data/lib/tms_client/resource/keyword.rb +22 -0
- data/lib/tms_client/resource/recipient.rb +8 -0
- data/lib/tms_client/resource/sms_message.rb +9 -0
- data/lib/tms_client/resource/voice_message.rb +13 -0
- data/lib/tms_client/util/core_ext.rb +27 -0
- data/lib/tms_client/util/hal_link_parser.rb +50 -0
- data/lib/tms_client/version.rb +3 -0
- data/lib/tms_client.rb +30 -0
- data/spec/client_spec.rb +25 -0
- data/spec/command_types_spec.rb +20 -0
- data/spec/inbound_messages_spec.rb +19 -0
- data/spec/keyword_spec.rb +58 -0
- data/spec/keywords_spec.rb +21 -0
- data/spec/message_spec.rb +55 -0
- data/spec/messages_spec.rb +21 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/tms_client_spec.rb +7 -0
- data/tms_client.gemspec +37 -0
- metadata +208 -0
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
TMS Client
|
2
|
+
===========
|
3
|
+
This is a ruby client to interact with the GovDelivery TMS REST API.
|
4
|
+
|
5
|
+
|
6
|
+
Usage
|
7
|
+
-----
|
8
|
+
### Connecting
|
9
|
+
|
10
|
+
``` ruby
|
11
|
+
client = TMS::Client.new('username', 'password', :api_root => 'https://tms.govdelivery.com')
|
12
|
+
|
13
|
+
```
|
14
|
+
|
15
|
+
### Getting messages
|
16
|
+
|
17
|
+
``` ruby
|
18
|
+
client.subresources #=> {"messages"=><TMS::Messages href=/messages collection=[]>}
|
19
|
+
client.messages #=> <TMS::Messages href=/messages collection=[]>
|
20
|
+
client.sms_messages.get #=> #lots of sms stuff
|
21
|
+
client.sms_messages.next #=> <TMS::Messages href=/messages/page/2 collection=[]>
|
22
|
+
# (if there is a second page)
|
23
|
+
client.sms_messages.next.get #=> # more messages...
|
24
|
+
client.voice_messages.get #=> #lots of voice stuff
|
25
|
+
client.voice_messages.next #=> <TMS::Messages href=/messages/page/2 collection=[]>
|
26
|
+
# (if there is a second page)
|
27
|
+
client.voice_messages.next.get #=> # more messages...
|
28
|
+
```
|
29
|
+
|
30
|
+
|
31
|
+
### Sending an SMS Message
|
32
|
+
|
33
|
+
``` ruby
|
34
|
+
message = client.sms_messages.build(:short_body=>'Test Message!')
|
35
|
+
message.recipients.build(:phone=>'5551112222')
|
36
|
+
message.recipients.build(:phone=>'5551112223')
|
37
|
+
message.recipients.build # invalid - no phone
|
38
|
+
message.post #=> true
|
39
|
+
message.recipients.collection.detect{|r| r.errors } #=> {"phone"=>["is not a number"]}
|
40
|
+
# save succeeded, but we have one bad recipient
|
41
|
+
message.href #=> "/messages/87"
|
42
|
+
message.get #=> <TMS::Message href=/messages/87 attributes={...}>
|
43
|
+
```
|
44
|
+
|
45
|
+
### Sending an Voice Message
|
46
|
+
|
47
|
+
``` ruby
|
48
|
+
message = client.voice_messages.build(:url=>'www.testmessage.com')
|
49
|
+
message.recipients.build(:phone=>'5551112222')
|
50
|
+
message.recipients.build(:phone=>'5551112223')
|
51
|
+
message.recipients.build # invalid - no phone
|
52
|
+
message.post #=> true
|
53
|
+
message.recipients.collection.detect{|r| r.errors } #=> {"phone"=>["is not a number"]}
|
54
|
+
# save succeeded, but we have one bad recipient
|
55
|
+
message.href #=> "/messages/87"
|
56
|
+
message.get #=> <TMS::Message href=/messages/87 attributes={...}>
|
57
|
+
```
|
58
|
+
|
59
|
+
### Listing Command Types
|
60
|
+
|
61
|
+
``` ruby
|
62
|
+
command_types = client.command_types.get
|
63
|
+
command_types.collection.each do |at|
|
64
|
+
puts at.name #=> "forward"
|
65
|
+
puts at.fields #=> ["url", "http_method", ...]
|
66
|
+
end
|
67
|
+
````
|
68
|
+
|
69
|
+
### Managing Keywords
|
70
|
+
|
71
|
+
``` ruby
|
72
|
+
# CRUD
|
73
|
+
keyword = client.keywords.build(:name => "BUSRIDE")
|
74
|
+
keyword.post #=> true
|
75
|
+
keyword.name #=> 'busride'
|
76
|
+
keyword.name = "TRAINRIDE"
|
77
|
+
keyword.put #=> true
|
78
|
+
keyword.name #=> 'trainride'
|
79
|
+
keyword.delete #=> true
|
80
|
+
|
81
|
+
# list
|
82
|
+
keywords = client.keywords.get
|
83
|
+
keywords.collection.each do |k|
|
84
|
+
puts k.name
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
### Managing Commands
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
# CRUD
|
92
|
+
keywords = client.keywords.get
|
93
|
+
keyword = keywords.collection.first.get
|
94
|
+
command = keyword.commands.build(
|
95
|
+
:name => "Forward to somewhere else",
|
96
|
+
:params => {:url => "http://example.com", :http_method => "get"},
|
97
|
+
:command_type => :forward)
|
98
|
+
command.post
|
99
|
+
command.params = {:url => "http://example.com/new_url", :http_method => "post"}
|
100
|
+
command.put
|
101
|
+
command.delete
|
102
|
+
|
103
|
+
# list
|
104
|
+
commands = keyword.commands.get
|
105
|
+
commands.collection.each do |c|
|
106
|
+
puts c.inspect
|
107
|
+
end
|
108
|
+
```
|
109
|
+
|
110
|
+
|
111
|
+
### Logging
|
112
|
+
Any instance of a [Logger](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html "Ruby Logger")-like class can be passed in to the client; incoming and outgoing
|
113
|
+
request information will then be logged to that instance.
|
114
|
+
|
115
|
+
The example below configures `TMS::Client` to log to STDOUT:
|
116
|
+
|
117
|
+
``` ruby
|
118
|
+
logger = Logger.new(STDOUT)
|
119
|
+
client = TMS::Client.new('username', 'password', :logger => logger)
|
120
|
+
|
121
|
+
```
|
122
|
+
|
123
|
+
License
|
124
|
+
-------
|
125
|
+
Copyright (c) 2013, GovDelivery, Inc.
|
126
|
+
|
127
|
+
All rights reserved.
|
128
|
+
|
129
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
130
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
131
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
132
|
+
* Neither the name of GovDelivery nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
133
|
+
|
134
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module TMS #:nodoc:
|
2
|
+
module Base
|
3
|
+
def self.included(base)
|
4
|
+
base.send(:include, TMS::Util::HalLinkParser)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
base.send(:include, InstanceMethods)
|
7
|
+
base.send(:include, TMS::CoreExt)
|
8
|
+
base.send(:extend, TMS::CoreExt)
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_accessor :client, :href, :errors
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def to_param
|
15
|
+
tmsify(self)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module InstanceMethods
|
20
|
+
def initialize(client, href)
|
21
|
+
self.client = client
|
22
|
+
self.href = href
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# The client class to connect and talk to the TMS REST API.
|
2
|
+
class TMS::Client
|
3
|
+
include TMS::Util::HalLinkParser
|
4
|
+
include TMS::CoreExt
|
5
|
+
|
6
|
+
attr_accessor :connection, :href
|
7
|
+
|
8
|
+
# Create a new client and issue a request for the available resources for a given account.
|
9
|
+
#
|
10
|
+
# === Options
|
11
|
+
# * +:api_root+ - The root URL of the TMS api. Defaults to localhost:3000
|
12
|
+
# * +:logger+ - An instance of a Logger class (http transport information will be logged here) - defaults to nil
|
13
|
+
#
|
14
|
+
# === Examples
|
15
|
+
# client = TMS::Client.new("foo@example.com", "onetwothree", {
|
16
|
+
# :api_root => "https://tms.govdelivery.com",
|
17
|
+
# :logger => Logger.new(STDOUT)})
|
18
|
+
#
|
19
|
+
def initialize(username, password, options = {:api_root => 'http://localhost:3000', :logger => nil})
|
20
|
+
@api_root = options[:api_root]
|
21
|
+
connect!(username, password, options[:logger])
|
22
|
+
discover!
|
23
|
+
end
|
24
|
+
|
25
|
+
def connect!(username, password, logger)
|
26
|
+
self.connection = TMS::Connection.new(:username => username, :password => password, :api_root => @api_root, :logger => logger)
|
27
|
+
end
|
28
|
+
|
29
|
+
def discover!
|
30
|
+
services = get('/').body
|
31
|
+
parse_links(services['_links'])
|
32
|
+
end
|
33
|
+
|
34
|
+
def get(href)
|
35
|
+
response = raw_connection.get(href)
|
36
|
+
case response.status
|
37
|
+
when 401..499
|
38
|
+
raise TMS::Request::Error.new(response.status)
|
39
|
+
when 202
|
40
|
+
raise TMS::Request::InProgress.new(response.body['message'])
|
41
|
+
else
|
42
|
+
return response
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def post(obj)
|
47
|
+
raw_connection.post do |req|
|
48
|
+
req.url @api_root + obj.href
|
49
|
+
req.headers['Content-Type'] = 'application/json'
|
50
|
+
req.body = obj.to_json
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def put(obj)
|
55
|
+
raw_connection.put do |req|
|
56
|
+
req.url @api_root + obj.href
|
57
|
+
req.headers['Content-Type'] = 'application/json'
|
58
|
+
req.body = obj.to_json
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def delete(href)
|
63
|
+
response = raw_connection.delete(href)
|
64
|
+
case response.status
|
65
|
+
when 200
|
66
|
+
return response
|
67
|
+
else
|
68
|
+
raise TMS::Request::Error.new(response.status)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def raw_connection
|
73
|
+
connection.connection
|
74
|
+
end
|
75
|
+
|
76
|
+
def client
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module TMS::CollectionResource
|
2
|
+
def self.included(base)
|
3
|
+
base.send(:include, InstanceMethods)
|
4
|
+
end
|
5
|
+
|
6
|
+
module InstanceMethods
|
7
|
+
include TMS::Base
|
8
|
+
attr_accessor :collection
|
9
|
+
|
10
|
+
def initialize(client, href, items=nil)
|
11
|
+
super(client, href)
|
12
|
+
if items
|
13
|
+
initialize_collection_from_items(items)
|
14
|
+
else
|
15
|
+
self.collection = []
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def get
|
21
|
+
response = client.get(href)
|
22
|
+
initialize_collection_from_items(response.body)
|
23
|
+
#setup page links from header
|
24
|
+
links = LinkHeader.parse(response.headers['link']).to_a.collect do |a|
|
25
|
+
{a[1][0].last => a[0]}
|
26
|
+
end
|
27
|
+
parse_links(links)
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def build(attributes=nil)
|
32
|
+
thing = instance_class(self.class).new(client, self.href, attributes || {})
|
33
|
+
self.collection << thing
|
34
|
+
thing
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_json
|
38
|
+
@collection.map(&:to_json)
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
"<#{self.class.inspect} href=#{self.href} collection=#{self.collection.inspect}>"
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def initialize_collection_from_items(items)
|
48
|
+
self.collection = items.map do |attrs|
|
49
|
+
instance_class(self.class).new(client, nil, attrs)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class TMS::Connection
|
2
|
+
attr_accessor :username, :password, :api_root, :connection, :logger
|
3
|
+
|
4
|
+
def get(href)
|
5
|
+
resp = connection.get("#{href}.json")
|
6
|
+
if resp.status != 200
|
7
|
+
raise RecordNotFound.new("Could not find resource at #{href} (status #{resp.status})")
|
8
|
+
else
|
9
|
+
resp.body
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(opts={})
|
14
|
+
self.username = opts[:username]
|
15
|
+
self.password = opts[:password]
|
16
|
+
self.api_root = opts[:api_root]
|
17
|
+
self.logger = opts[:logger]
|
18
|
+
setup_connection
|
19
|
+
end
|
20
|
+
|
21
|
+
def setup_connection
|
22
|
+
self.connection = Faraday.new(:url => self.api_root) do |faraday|
|
23
|
+
faraday.use TMS::Logger, self.logger if self.logger
|
24
|
+
faraday.request :json
|
25
|
+
faraday.basic_auth(self.username, self.password)
|
26
|
+
faraday.response :json, :content_type => /\bjson$/
|
27
|
+
faraday.adapter :net_http
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
module TMS::InstanceResource
|
2
|
+
def self.included(base)
|
3
|
+
base.send(:include, TMS::Base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
base.send(:include, InstanceMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
#
|
10
|
+
# Writeable attributes are sent on POST/PUT.
|
11
|
+
#
|
12
|
+
def writeable_attributes(*attrs)
|
13
|
+
@writeable_attributes ||= []
|
14
|
+
if attrs.any?
|
15
|
+
@writeable_attributes.map!(&:to_sym).concat(attrs).uniq! if attrs.any?
|
16
|
+
setup_attributes(@writeable_attributes, false)
|
17
|
+
end
|
18
|
+
@writeable_attributes
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# Readonly attributes don't get POSTed.
|
23
|
+
# (timestamps are included by default)
|
24
|
+
#
|
25
|
+
def readonly_attributes(*attrs)
|
26
|
+
@readonly_attributes ||= [:created_at, :updated_at, :completed_at]
|
27
|
+
if attrs.any?
|
28
|
+
@readonly_attributes.map!(&:to_sym).concat(attrs).uniq!
|
29
|
+
setup_attributes(@readonly_attributes, true)
|
30
|
+
end
|
31
|
+
@readonly_attributes
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# For collections that are represented as attributes (i.e. inline, no href)
|
36
|
+
#
|
37
|
+
# # message.rb
|
38
|
+
# collection_attributes :recipients
|
39
|
+
def collection_attributes(*attrs)
|
40
|
+
@collection_attributes ||= []
|
41
|
+
if attrs.any?
|
42
|
+
@collection_attributes.map!(&:to_sym).concat(attrs).uniq! if attrs.any?
|
43
|
+
@collection_attributes.each { |a| setup_collection(a) }
|
44
|
+
end
|
45
|
+
@collection_attributes
|
46
|
+
end
|
47
|
+
|
48
|
+
def custom_class_names
|
49
|
+
@custom_class_names ||= {}
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# For collections that are represented as attributes (i.e. inline, no href)
|
54
|
+
# and that have a class name other than the one we would infer.
|
55
|
+
#
|
56
|
+
# # email.rb
|
57
|
+
# collection_attributes :recipients, 'EmailRecipient'
|
58
|
+
def collection_attribute(attr, tms_class)
|
59
|
+
@collection_attributes ||= []
|
60
|
+
@collection_attributes.push(attr).uniq!
|
61
|
+
setup_collection(attr, TMS.const_get(tms_class))
|
62
|
+
end
|
63
|
+
|
64
|
+
def setup_attributes(attrs, readonly=false)
|
65
|
+
attrs.map(&:to_sym).each do |property|
|
66
|
+
self.send :define_method, :"#{property}=", &lambda { |v| @attributes[property] = v } unless readonly
|
67
|
+
self.send :define_method, property.to_sym, &lambda { @attributes[property] }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def setup_collection(property, klass=nil)
|
72
|
+
if klass
|
73
|
+
custom_class_names[property] = klass
|
74
|
+
else
|
75
|
+
klass ||= TMS.const_get(property.to_s.capitalize)
|
76
|
+
end
|
77
|
+
|
78
|
+
self.send :define_method, property.to_sym, &lambda { @attributes[property] ||= klass.new(self.client, nil, nil) }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
module InstanceMethods
|
83
|
+
def initialize(client, href=nil, attrs=nil)
|
84
|
+
super(client, href)
|
85
|
+
@attributes = {}
|
86
|
+
attrs ||= self.client.get(href).body if href
|
87
|
+
set_attributes_from_hash(attrs) if attrs
|
88
|
+
end
|
89
|
+
|
90
|
+
def get
|
91
|
+
set_attributes_from_hash(self.client.get(href).body)
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
def post
|
96
|
+
response = client.post(self)
|
97
|
+
case response.status
|
98
|
+
when 201
|
99
|
+
set_attributes_from_hash(response.body)
|
100
|
+
return true
|
101
|
+
when 401
|
102
|
+
raise Exception.new("401 Not Authorized")
|
103
|
+
when 404
|
104
|
+
raise(Exception.new("Can't POST to #{self.href}"))
|
105
|
+
else
|
106
|
+
if response.body['errors']
|
107
|
+
self.errors = response.body['errors']
|
108
|
+
end
|
109
|
+
end
|
110
|
+
return false
|
111
|
+
end
|
112
|
+
|
113
|
+
def put
|
114
|
+
response = client.put(self)
|
115
|
+
case response.status
|
116
|
+
when 200
|
117
|
+
set_attributes_from_hash(response.body)
|
118
|
+
return true
|
119
|
+
when 401
|
120
|
+
raise Exception.new("401 Not Authorized")
|
121
|
+
when 404
|
122
|
+
raise(Exception.new("Can't POST to #{self.href}"))
|
123
|
+
else
|
124
|
+
if response.body['errors']
|
125
|
+
self.errors = response.body['errors']
|
126
|
+
end
|
127
|
+
end
|
128
|
+
return false
|
129
|
+
end
|
130
|
+
|
131
|
+
def delete
|
132
|
+
response = self.client.delete(href)
|
133
|
+
case response.status
|
134
|
+
when 200
|
135
|
+
return true
|
136
|
+
else
|
137
|
+
if response.body['errors']
|
138
|
+
self.errors = response.body['errors']
|
139
|
+
end
|
140
|
+
end
|
141
|
+
return false
|
142
|
+
end
|
143
|
+
|
144
|
+
def to_s
|
145
|
+
"<#{self.class.inspect}#{' href=' + self.href if self.href} attributes=#{@attributes.inspect}>"
|
146
|
+
end
|
147
|
+
|
148
|
+
def to_json
|
149
|
+
json_hash = {}
|
150
|
+
self.class.writeable_attributes.each do |attr|
|
151
|
+
json_hash[attr] = self.send(attr)
|
152
|
+
end
|
153
|
+
self.class.collection_attributes.each do |coll|
|
154
|
+
json_hash[coll] = self.send(coll).to_json
|
155
|
+
end
|
156
|
+
json_hash
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def set_attributes_from_hash(hash)
|
162
|
+
hash.reject { |k, _| k=~/^_/ }.each do |property, value|
|
163
|
+
if self.class.collection_attributes.include?(property.to_sym)
|
164
|
+
klass = self.class.custom_class_names[property] || TMS.const_get(property.to_s.capitalize)
|
165
|
+
@attributes[property.to_sym] = klass.new(client, nil, value)
|
166
|
+
else
|
167
|
+
@attributes[property.to_sym] = value
|
168
|
+
end
|
169
|
+
end
|
170
|
+
self.errors = hash['errors']
|
171
|
+
parse_links(hash['_links'])
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module TMS #:nodoc:
|
2
|
+
class Logger < Faraday::Response::Middleware #:nodoc:
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def initialize(app, logger = nil)
|
6
|
+
super(app)
|
7
|
+
@logger = logger || begin
|
8
|
+
require 'logger'
|
9
|
+
::Logger.new(STDOUT)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def_delegators :@logger, :debug, :info, :warn, :error, :fatal
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
puts env.inspect
|
17
|
+
info "#{env[:method]} #{env[:url].to_s}"
|
18
|
+
debug('request') { dump_headers env[:request_headers] }
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_complete(env)
|
23
|
+
info('Status') { env[:status].to_s }
|
24
|
+
debug('response headers') { dump_headers env[:response_headers] }
|
25
|
+
debug('response body') { env[:body].inspect }
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def dump_headers(headers)
|
31
|
+
headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module TMS #:nodoc:
|
2
|
+
module Request
|
3
|
+
# The generic TMS error class
|
4
|
+
class Error < StandardError
|
5
|
+
attr_reader :code
|
6
|
+
|
7
|
+
def initialize(code)
|
8
|
+
super("HTTP Error: #{code}")
|
9
|
+
@code=code
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Raised when a recipient list is still being constructed and a request is made to view the
|
14
|
+
# recipient list for a message.
|
15
|
+
class InProgress < StandardError; end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class TMS::Emails
|
2
|
+
include TMS::CollectionResource
|
3
|
+
end
|
4
|
+
|
5
|
+
class TMS::VoiceMessages
|
6
|
+
include TMS::CollectionResource
|
7
|
+
end
|
8
|
+
|
9
|
+
class TMS::SmsMessages
|
10
|
+
include TMS::CollectionResource
|
11
|
+
end
|
12
|
+
|
13
|
+
class TMS::Emails
|
14
|
+
include TMS::CollectionResource
|
15
|
+
end
|
16
|
+
|
17
|
+
class TMS::Recipients
|
18
|
+
include TMS::CollectionResource
|
19
|
+
end
|
20
|
+
|
21
|
+
class TMS::EmailRecipients
|
22
|
+
include TMS::CollectionResource
|
23
|
+
end
|
24
|
+
|
25
|
+
# A collection of Keyword objects.
|
26
|
+
#
|
27
|
+
# === Example
|
28
|
+
# keywords = client.keywords.get
|
29
|
+
#
|
30
|
+
class TMS::Keywords
|
31
|
+
include TMS::CollectionResource
|
32
|
+
end
|
33
|
+
|
34
|
+
class TMS::InboundMessages
|
35
|
+
include TMS::CollectionResource
|
36
|
+
end
|
37
|
+
|
38
|
+
# A collection of CommandType instances.
|
39
|
+
# This resource changes infrequently. It may be used to dynamically construct a
|
40
|
+
# user interface for configuring arbitrary SMS keywords for an account.
|
41
|
+
#
|
42
|
+
# This resource is read-only.
|
43
|
+
#
|
44
|
+
# === Example
|
45
|
+
# client.command_types.get
|
46
|
+
# client.command_types.collection.each {|at| ... }
|
47
|
+
class TMS::CommandTypes
|
48
|
+
include TMS::CollectionResource
|
49
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module TMS #:nodoc:
|
2
|
+
# A command is a combination of behavior and parameters that should be executed
|
3
|
+
# when an incoming SMS message matches the associated Keyword.
|
4
|
+
#
|
5
|
+
# ==== Attributes
|
6
|
+
#
|
7
|
+
# * +name+ - The name of the command. This will default to the command_type if not supplied.
|
8
|
+
# * +command_type+ - The type of this command. A list of valid types can be found by querying the CommandType list.
|
9
|
+
# * +params+ - A Hash of string/string pairs used as configuration for this command.
|
10
|
+
#
|
11
|
+
# === Examples
|
12
|
+
# command = keyword.commands.build(:name => "subscribe to news", :command_type => "dcm_subscribe", :dcm_account_code => "NEWS", :dcm_topic_codes => "NEWS_1, NEWS_2")
|
13
|
+
# command.post
|
14
|
+
# command.dcm_topic_codes += ", NEWS_5"
|
15
|
+
# command.put
|
16
|
+
# command.delete
|
17
|
+
class Command
|
18
|
+
include InstanceResource
|
19
|
+
|
20
|
+
writeable_attributes :name, :command_type, :params
|
21
|
+
readonly_attributes :created_at, :updated_at
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module TMS #:nodoc:
|
2
|
+
# CommandType is a pair of values (name, fields) that can be attached
|
3
|
+
# to a Keyword (in a Command object).
|
4
|
+
#
|
5
|
+
# This resource is read-only.
|
6
|
+
#
|
7
|
+
# ==== Attributes
|
8
|
+
#
|
9
|
+
# * +name+ - The name of the CommandType.
|
10
|
+
# * +fields+ - An Array of strings representing the different fields on this
|
11
|
+
# CommandType. Field values will always be strings.
|
12
|
+
#
|
13
|
+
class CommandType
|
14
|
+
|
15
|
+
include InstanceResource
|
16
|
+
|
17
|
+
readonly_attributes :name, :fields
|
18
|
+
end
|
19
|
+
end
|