essential 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +35 -0
- data/LICENSE +21 -0
- data/README.md +107 -0
- data/Rakefile +9 -0
- data/essential.gemspec +26 -0
- data/lib/essential.rb +32 -0
- data/lib/essential/account.rb +56 -0
- data/lib/essential/client.rb +65 -0
- data/lib/essential/errors/api_error.rb +54 -0
- data/lib/essential/messaging/channel.rb +36 -0
- data/lib/essential/messaging/message.rb +68 -0
- data/lib/essential/messaging/property.rb +27 -0
- data/lib/essential/messaging/subscriber.rb +64 -0
- data/lib/essential/messaging/transport.rb +29 -0
- data/lib/essential/resource.rb +127 -0
- data/lib/essential/resource/attr_methods.rb +65 -0
- data/lib/essential/resource/attr_relations.rb +81 -0
- data/lib/essential/resource/create.rb +21 -0
- data/lib/essential/resource/delete.rb +20 -0
- data/lib/essential/resource/list.rb +17 -0
- data/lib/essential/resource/paginator_proxy.rb +115 -0
- data/lib/essential/resource/update.rb +26 -0
- data/lib/essential/version.rb +3 -0
- data/test/essential/account_test.rb +22 -0
- data/test/essential/messaging/channel_test.rb +62 -0
- data/test/essential/messaging/message_test.rb +119 -0
- data/test/essential/messaging/property_test.rb +27 -0
- data/test/essential/messaging/subscriber_test.rb +106 -0
- data/test/essential/messaging/transport_test.rb +37 -0
- data/test/integration/alternate_authentication_test.rb +128 -0
- data/test/test_helper.rb +34 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7fef856a4598b665b03c68baaf34c34eb79a8d8d
|
4
|
+
data.tar.gz: 81c1e44539dfbe8b6dc59dd7f61165640247a2a7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2f81a83c772e1190736131d8c99444538b1451b2ce609625e4528474584e992eb78f9d2cb45ab0067ddedb15e3a8dafede902da1c3233cba1db48b0c3233d803
|
7
|
+
data.tar.gz: 061610d3b785a2d8ee7d9dffa12605b93c9da1a59c6401b6668a170f420c534727a2480e3519b175450272160a3fe47d4f3c9e057003213572a11e5cd37da373
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
essential (0.1.0)
|
5
|
+
rest-client (~> 1.8)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://www.rubygems.org/
|
9
|
+
specs:
|
10
|
+
domain_name (0.5.20160128)
|
11
|
+
unf (>= 0.0.5, < 1.0.0)
|
12
|
+
http-cookie (1.0.2)
|
13
|
+
domain_name (~> 0.5)
|
14
|
+
mime-types (2.99.1)
|
15
|
+
minitest (5.8.4)
|
16
|
+
netrc (0.11.0)
|
17
|
+
rake (10.5.0)
|
18
|
+
rest-client (1.8.0)
|
19
|
+
http-cookie (>= 1.0.2, < 2.0)
|
20
|
+
mime-types (>= 1.16, < 3.0)
|
21
|
+
netrc (~> 0.7)
|
22
|
+
unf (0.1.4)
|
23
|
+
unf_ext
|
24
|
+
unf_ext (0.0.7.2)
|
25
|
+
|
26
|
+
PLATFORMS
|
27
|
+
ruby
|
28
|
+
|
29
|
+
DEPENDENCIES
|
30
|
+
essential!
|
31
|
+
minitest (~> 5.8.4)
|
32
|
+
rake
|
33
|
+
|
34
|
+
BUNDLED WITH
|
35
|
+
1.11.2
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016- Essential Platform Services, Inc. (https://essential.to)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# essential-ruby
|
2
|
+
|
3
|
+
## Quick Start
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
require 'essential'
|
7
|
+
|
8
|
+
Essential.sid = ENV['ESSENTIAL_SID']
|
9
|
+
Essential.token = ENV['ESSENTIAL_TOKEN']
|
10
|
+
|
11
|
+
default_channel = Essential::Messaging::Channel.list.first
|
12
|
+
|
13
|
+
message = default_channel.messages.create(
|
14
|
+
subscriber: '2065551212',
|
15
|
+
body: "Hello there."
|
16
|
+
)
|
17
|
+
puts message.sid
|
18
|
+
|
19
|
+
```
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
### Initial Setup
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require 'essential'
|
27
|
+
# Essential.api_base = 'http://127.0.0.1:3000'
|
28
|
+
Essential.sid = ENV['ESSENTIAL_SID']
|
29
|
+
Essential.token = ENV['ESSENTIAL_TOKEN']
|
30
|
+
|
31
|
+
```
|
32
|
+
|
33
|
+
### Account
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
account = Essential::Account.retrieve
|
37
|
+
```
|
38
|
+
|
39
|
+
### Channels
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
account.channels.map{|ch| {ch.sid => ch.name}}.reduce(&:merge)
|
43
|
+
# => {"ch_p03Gjl8Uzn0RkZSpHXHnrw"=>"default", "ch_QsbrYCSo2m0sShMLOlZqtw"=>"yradnoces"}
|
44
|
+
channel = Essential::Messaging::Channel.retrieve('ch_p03Gjl8Uzn0RkZSpHXHnrw')
|
45
|
+
channel.update(name: 'default', onreceived_url: 'http:/api.example.com/hook')
|
46
|
+
```
|
47
|
+
|
48
|
+
### Subscribers
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
channel = Essential::Messaging::Channel.list.first
|
52
|
+
|
53
|
+
# create a subscriber
|
54
|
+
subscriber = channel.subscribers.create(phone_number: '2065551212')
|
55
|
+
puts subscriber.sid
|
56
|
+
# => sub_WfnIr-f-7FBMFr4nVKkMdg
|
57
|
+
|
58
|
+
# unsubscribe
|
59
|
+
subscriber.unsubscribe
|
60
|
+
|
61
|
+
# re-subscribe
|
62
|
+
subscriber = channel.subscribers.create(phone_number: '2065551212')
|
63
|
+
puts subscriber.sid
|
64
|
+
# => sub_WfnIr-f-7FBMFr4nVKkMdg
|
65
|
+
```
|
66
|
+
|
67
|
+
### Messages
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
# This example magically finds the correct channel
|
71
|
+
# by either using an existing subscriber, or falling
|
72
|
+
# back on the channel named 'default'.
|
73
|
+
message = Essential::Messaging::Message.create(
|
74
|
+
subscriber: '2065551212',
|
75
|
+
body: 'magic channel'
|
76
|
+
)
|
77
|
+
|
78
|
+
# Here we specify a subscriber explicitly
|
79
|
+
message = Essential::Messaging::Message.create(
|
80
|
+
subscriber: 'sub_pDTv7LSV-R-AzL7oEpBfoQ',
|
81
|
+
body: 'subscriber by sid'
|
82
|
+
)
|
83
|
+
|
84
|
+
# Here we specify a channel
|
85
|
+
message = Essential::Messaging::Message.create(
|
86
|
+
subscriber: '2065551212',
|
87
|
+
channel: 'yradnoces',
|
88
|
+
body: 'explicit channel'
|
89
|
+
)
|
90
|
+
```
|
91
|
+
|
92
|
+
### Properties
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
subscriber = channel.subscribers.create(phone_number: '2065551212')
|
96
|
+
|
97
|
+
# set new properties
|
98
|
+
subscriber.properties.create(name: 'external_id', value: 1234)
|
99
|
+
subscriber.properties.create(name: 'name', value: 'Brendan Ribera')
|
100
|
+
subscriber.properties.create(name: 'state', value: 'WA')
|
101
|
+
|
102
|
+
# print them
|
103
|
+
puts JSON.pretty_generate(subscriber.properties.as_json)
|
104
|
+
|
105
|
+
# update one
|
106
|
+
subscriber.properties.create(name: 'name', value: 'Nabnerb Arebir')
|
107
|
+
```
|
data/Rakefile
ADDED
data/essential.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
2
|
+
|
3
|
+
require 'essential/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'essential'
|
7
|
+
s.version = Essential::VERSION
|
8
|
+
s.date = '2016-04-06'
|
9
|
+
s.summary = 'Essential Ruby Library'
|
10
|
+
s.description = 'A GEM for interacting with the Essential.to service.'
|
11
|
+
s.authors = ['Brendan Ribera']
|
12
|
+
s.email = 'brendan@madronavl.com'
|
13
|
+
s.homepage = 'http://github.com/madrona-labs/essential-ruby'
|
14
|
+
s.license = 'MIT'
|
15
|
+
|
16
|
+
s.add_dependency('rest-client', '~> 1.8')
|
17
|
+
|
18
|
+
s.add_development_dependency('rake', '~> 10.5')
|
19
|
+
s.add_development_dependency('minitest', '~> 5.8')
|
20
|
+
|
21
|
+
s.files = `git ls-files`.split("\n")
|
22
|
+
s.test_files = `git ls-files -- test/*`.split("\n")
|
23
|
+
# s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
24
|
+
|
25
|
+
s.require_paths = ['lib']
|
26
|
+
end
|
data/lib/essential.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'rest-client'
|
3
|
+
require 'set'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
require 'essential/version'
|
7
|
+
require 'essential/client'
|
8
|
+
|
9
|
+
# Resources
|
10
|
+
require 'essential/resource'
|
11
|
+
require 'essential/resource/paginator_proxy'
|
12
|
+
|
13
|
+
# Top-level Resources
|
14
|
+
require 'essential/account'
|
15
|
+
|
16
|
+
# Messaging Resources
|
17
|
+
require 'essential/messaging/message'
|
18
|
+
require 'essential/messaging/property'
|
19
|
+
require 'essential/messaging/transport'
|
20
|
+
require 'essential/messaging/subscriber'
|
21
|
+
require 'essential/messaging/channel'
|
22
|
+
|
23
|
+
module Essential
|
24
|
+
@api_base = 'https://api.essential.to'
|
25
|
+
@utc_offset = nil
|
26
|
+
|
27
|
+
class << self
|
28
|
+
attr_accessor :api_base, :sid, :token, :utc_offset
|
29
|
+
end
|
30
|
+
|
31
|
+
include Client
|
32
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Essential
|
2
|
+
class Account < Resource
|
3
|
+
include Essential::Resource::Update
|
4
|
+
|
5
|
+
attr_property :name, :token, :unsubscribed_send_raises, :created_at, :updated_at
|
6
|
+
attr_schema created_at: Time, updated_at: Time
|
7
|
+
|
8
|
+
def self.retrieve(opts={}, headers: {})
|
9
|
+
me = self.new(headers: headers)
|
10
|
+
me.fetch
|
11
|
+
me
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.url
|
15
|
+
"/v2/account"
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.api_status(params: {}, headers: @headers)
|
19
|
+
# permitted:
|
20
|
+
# :start_date, :end_date
|
21
|
+
resp = self.request(
|
22
|
+
:get,
|
23
|
+
url: '/v2/account/analytics/api_status',
|
24
|
+
params: params,
|
25
|
+
headers: headers
|
26
|
+
)
|
27
|
+
JSON.parse(resp)
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(sid: nil, headers: nil)
|
31
|
+
sid ||= Essential.sid
|
32
|
+
|
33
|
+
super(sid: sid, headers: headers)
|
34
|
+
end
|
35
|
+
|
36
|
+
def url
|
37
|
+
self.class.url
|
38
|
+
end
|
39
|
+
|
40
|
+
def channels
|
41
|
+
Essential::Resource::PaginatorProxy.new(Essential::Messaging::Channel, headers: @headers)
|
42
|
+
end
|
43
|
+
|
44
|
+
def subscribers
|
45
|
+
Essential::Resource::PaginatorProxy.new(Essential::Messaging::Subscriber, headers: @headers)
|
46
|
+
end
|
47
|
+
|
48
|
+
def transports
|
49
|
+
Essential::Resource::PaginatorProxy.new(Essential::Messaging::Transport, headers: @headers)
|
50
|
+
end
|
51
|
+
|
52
|
+
def messages
|
53
|
+
Essential::Resource::PaginatorProxy.new(Essential::Messaging::Message, headers: @headers)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'essential/errors/api_error'
|
2
|
+
|
3
|
+
module Essential
|
4
|
+
module Client
|
5
|
+
module ClassMethods
|
6
|
+
def request(method, url, sid: nil, token: nil, params: {}, headers: {})
|
7
|
+
# GET -> :get, etc
|
8
|
+
method = method.to_s.downcase.to_sym
|
9
|
+
|
10
|
+
sid ||= self.sid
|
11
|
+
token ||= self.token
|
12
|
+
|
13
|
+
uri = URI.join(self. api_base, url)
|
14
|
+
|
15
|
+
headers = {
|
16
|
+
'Content-Type' => 'application/json',
|
17
|
+
'Accept' => 'application/json',
|
18
|
+
'User-Agent' => user_agent(),
|
19
|
+
}.merge(headers || {})
|
20
|
+
|
21
|
+
case method
|
22
|
+
when :get, :delete
|
23
|
+
uri.query = URI.encode_www_form(params) if params && params.any?
|
24
|
+
else
|
25
|
+
payload = params.to_json
|
26
|
+
end
|
27
|
+
|
28
|
+
opts = {
|
29
|
+
method: method,
|
30
|
+
url: uri.to_s,
|
31
|
+
user: sid,
|
32
|
+
password: token,
|
33
|
+
timeout: 10, # TODO config
|
34
|
+
open_timeout: 10, # TODO: config
|
35
|
+
headers: headers,
|
36
|
+
payload: payload
|
37
|
+
}
|
38
|
+
|
39
|
+
if $debug
|
40
|
+
puts format('%s %s', method.to_s.upcase, uri)
|
41
|
+
puts format('opts: %s', JSON.pretty_generate(opts))
|
42
|
+
end
|
43
|
+
|
44
|
+
begin
|
45
|
+
response = RestClient::Request.execute(opts)
|
46
|
+
rescue StandardError => e
|
47
|
+
raise Essential::APIError.from_exception(e)
|
48
|
+
end
|
49
|
+
|
50
|
+
response
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def user_agent
|
56
|
+
format('Essential Ruby (%s)', Essential::VERSION)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.included(base)
|
61
|
+
base.extend(ClassMethods)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Essential
|
2
|
+
class APIError < StandardError
|
3
|
+
private_class_method :new
|
4
|
+
|
5
|
+
attr_reader :http_status, :body
|
6
|
+
attr_reader :type, :message, :code, :params
|
7
|
+
|
8
|
+
def self.from_exception(e)
|
9
|
+
case e
|
10
|
+
# when SocketError
|
11
|
+
# when NoMethodError
|
12
|
+
when RestClient::ExceptionWithResponse
|
13
|
+
new(
|
14
|
+
http_status: e.http_code,
|
15
|
+
body: e.http_body
|
16
|
+
)
|
17
|
+
when RestClient::Exception
|
18
|
+
new(http_status: e.http_code)
|
19
|
+
# when Errno::ECONNREFUSED
|
20
|
+
else
|
21
|
+
raise e
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(http_status: nil, body: nil)
|
26
|
+
@http_status = http_status
|
27
|
+
|
28
|
+
if body
|
29
|
+
begin
|
30
|
+
@body = JSON.parse(body)
|
31
|
+
if @body.key?('error')
|
32
|
+
error = @body['error']
|
33
|
+
@type = error['type']
|
34
|
+
@message = error['message']
|
35
|
+
@code = error['code']
|
36
|
+
@params = error['params']
|
37
|
+
end
|
38
|
+
rescue JSON::ParserError
|
39
|
+
@body = body
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
parts = []
|
46
|
+
parts << format('<%s>', self.http_status)
|
47
|
+
parts << self.type
|
48
|
+
parts << self.message if self.message
|
49
|
+
parts << self.params.to_json if self.params
|
50
|
+
parts.join(' - ')
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Essential::Messaging
|
2
|
+
class Channel < Essential::Resource
|
3
|
+
extend Essential::Resource::List
|
4
|
+
include Essential::Resource::Update
|
5
|
+
|
6
|
+
attr_property :name, :onreceived_url, :created_at, :updated_at
|
7
|
+
attr_schema created_at: Time, updated_at: Time
|
8
|
+
|
9
|
+
attr_relation account_sid: 'Essential::Account'
|
10
|
+
|
11
|
+
def transports
|
12
|
+
Essential::Resource::PaginatorProxy.new(
|
13
|
+
Transport,
|
14
|
+
params: {channel: self.sid},
|
15
|
+
headers: @headers
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def subscribers
|
20
|
+
Essential::Resource::PaginatorProxy.new(
|
21
|
+
Subscriber,
|
22
|
+
params: {channel: self.sid},
|
23
|
+
headers: @headers
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def messages
|
28
|
+
Essential::Resource::PaginatorProxy.new(
|
29
|
+
Message,
|
30
|
+
params: {channel: self.sid},
|
31
|
+
headers: @headers
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|