twilio-lookups 0.0.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.
@@ -0,0 +1,40 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'builder'
4
+ require 'multi_json'
5
+ require 'cgi'
6
+ require 'openssl'
7
+ require 'base64'
8
+ require 'forwardable'
9
+ require 'jwt'
10
+
11
+ require 'twilio-lookups/version' unless defined?(Twilio::VERSION)
12
+ require 'twilio-lookups/util'
13
+ require 'twilio-lookups/rest/utils'
14
+ require 'twilio-lookups/rest/errors'
15
+ require 'twilio-lookups/util/client_config'
16
+ require 'twilio-lookups/rest/lookups_client'
17
+ require 'twilio-lookups/rest/instance_resource'
18
+ require 'twilio-lookups/rest/list_resource'
19
+ require 'twilio-lookups/rest/next_gen_list_resource'
20
+ require 'twilio-lookups/rest/lookups/phone_numbers'
21
+
22
+ module TwilioLookups
23
+ extend SingleForwardable
24
+
25
+ def_delegators :configuration, :account_sid, :auth_token
26
+
27
+ ##
28
+ # Pre-configure with account SID and auth token so that you don't need to
29
+ # pass them to various initializers each time.
30
+ def self.configure(&block)
31
+ yield configuration
32
+ end
33
+
34
+ ##
35
+ # Returns an existing or instantiates a new configuration object.
36
+ def self.configuration
37
+ @configuration ||= Util::Configuration.new
38
+ end
39
+ private_class_method :configuration
40
+ end
@@ -0,0 +1,130 @@
1
+ module TwilioLookups
2
+ module REST
3
+ class BaseClient
4
+ include TwilioLookups::Util
5
+ include TwilioLookups::REST::Utils
6
+
7
+ HTTP_HEADERS = {
8
+ 'Accept' => 'application/json',
9
+ 'Accept-Charset' => 'utf-8',
10
+ 'User-Agent' => "twilio-ruby/#{TwilioLookups::VERSION}" \
11
+ " (#{RUBY_ENGINE}/#{RUBY_PLATFORM}" \
12
+ " #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL})"
13
+ }
14
+
15
+ ##
16
+ # Override the default host for a REST Client (api.twilio.com)
17
+ def self.host(host=nil)
18
+ return @host unless host
19
+ @host = host
20
+ end
21
+
22
+ attr_reader :account_sid, :last_request, :last_response
23
+
24
+ def initialize(*args)
25
+ options = args.last.is_a?(Hash) ? args.pop : {}
26
+ options[:host] ||= self.class.host
27
+ @config = TwilioLookups::Util::ClientConfig.new options
28
+
29
+ @account_sid = args[0] || TwilioLookups.account_sid
30
+ @auth_token = args[1] || TwilioLookups.auth_token
31
+ if @account_sid.nil? || @auth_token.nil?
32
+ raise ArgumentError, 'Account SID and auth token are required'
33
+ end
34
+
35
+ set_up_connection
36
+ set_up_subresources
37
+ end
38
+
39
+ ##
40
+ # Define #get, #put, #post and #delete helper methods for sending HTTP
41
+ # requests to TwilioLookups. You shouldn't need to use these methods directly,
42
+ # but they can be useful for debugging. Each method returns a hash
43
+ # obtained from parsing the JSON object in the response body.
44
+ [:get, :put, :post, :delete].each do |method|
45
+ method_class = Net::HTTP.const_get method.to_s.capitalize
46
+ define_method method do |path, *args|
47
+ params = twilify(args[0])
48
+ params = {} if params.empty?
49
+ # build the full path unless already given
50
+ path = build_full_path(path, params, method) unless args[1]
51
+ request = method_class.new(path, HTTP_HEADERS)
52
+ request.basic_auth(@account_sid, @auth_token)
53
+ request.form_data = params if [:post, :put].include?(method)
54
+ connect_and_send(request)
55
+ end
56
+ end
57
+
58
+ protected
59
+
60
+ ##
61
+ # Builds up full request path
62
+ # Needs implementation in child classes
63
+ def build_full_path(path, params, method)
64
+ raise NotImplementedError
65
+ end
66
+
67
+ ##
68
+ # Set up and cache a Net::HTTP object to use when making requests. This is
69
+ # a private method documented for completeness.
70
+ def set_up_connection # :doc:
71
+ connection_class = Net::HTTP::Proxy @config.proxy_addr,
72
+ @config.proxy_port, @config.proxy_user, @config.proxy_pass
73
+ @connection = connection_class.new @config.host, @config.port
74
+ set_up_ssl
75
+ @connection.open_timeout = @config.timeout
76
+ @connection.read_timeout = @config.timeout
77
+ end
78
+
79
+ ##
80
+ # Set up the ssl properties of the <tt>@connection</tt> Net::HTTP object.
81
+ # This is a private method documented for completeness.
82
+ def set_up_ssl # :doc:
83
+ @connection.use_ssl = @config.use_ssl
84
+ if @config.ssl_verify_peer
85
+ @connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
86
+ @connection.ca_file = @config.ssl_ca_file
87
+ else
88
+ @connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
89
+ end
90
+ end
91
+
92
+ ##
93
+ # Set up sub resources attributes.
94
+ def set_up_subresources # :doc:
95
+ # To be overridden
96
+ end
97
+
98
+ ##
99
+ # Send an HTTP request using the cached <tt>@connection</tt> object and
100
+ # return the JSON response body parsed into a hash. Also save the raw
101
+ # Net::HTTP::Request and Net::HTTP::Response objects as
102
+ # <tt>@last_request</tt> and <tt>@last_response</tt> to allow for
103
+ # inspection later.
104
+ def connect_and_send(request) # :doc:
105
+ @last_request = request
106
+ retries_left = @config.retry_limit
107
+ begin
108
+ response = @connection.request request
109
+ @last_response = response
110
+ if response.kind_of? Net::HTTPServerError
111
+ raise TwilioLookups::REST::ServerError
112
+ end
113
+ rescue
114
+ raise if request.class == Net::HTTP::Post
115
+ if retries_left > 0 then retries_left -= 1; retry else raise end
116
+ end
117
+ if response.body and !response.body.empty?
118
+ object = MultiJson.load response.body
119
+ elsif response.kind_of? Net::HTTPBadRequest
120
+ object = { message: 'Bad request', code: 400 }
121
+ end
122
+
123
+ if response.kind_of? Net::HTTPClientError
124
+ raise TwilioLookups::REST::RequestError.new object['message'], object['code']
125
+ end
126
+ object
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,14 @@
1
+ module TwilioLookups
2
+ module REST
3
+ class ServerError < StandardError; end
4
+
5
+ class RequestError < StandardError
6
+ attr_reader :code
7
+
8
+ def initialize(message, code=nil);
9
+ super message
10
+ @code = code
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,87 @@
1
+ module TwilioLookups
2
+ module REST
3
+ ##
4
+ # A class to wrap an instance resource (like a call or application) within
5
+ # the Twilio API. All other instance resource classes within this library
6
+ # inherit from this class. You shouldn't need to instantiate this class
7
+ # directly. But reviewing the available methods is informative since they
8
+ # are rarely overridden in the inheriting class.
9
+ class InstanceResource
10
+ include Utils
11
+
12
+ ##
13
+ # Instantiate a new instance resource object. You must pass the +path+ of
14
+ # the instance (e.g. /2010-04-01/Accounts/AC123/Calls/CA456) as well as a
15
+ # +client+ object that responds to #get #post and #delete. This client
16
+ # is meant to be an instance of Twilio::REST::Client but could just as
17
+ # well be a mock object if you want to test the interface. The optional
18
+ # +params+ hash will be converted into attributes on the instantiated
19
+ # object.
20
+ def initialize(path, client, params = {})
21
+ @path, @client = path, client
22
+ set_up_properties_from params
23
+ end
24
+
25
+ def inspect # :nodoc:
26
+ "<#{self.class} @path=#{@path}>"
27
+ end
28
+
29
+ ##
30
+ # Update the properties of this instance resource using the key/value
31
+ # pairs in +params+. This makes an HTTP POST request to <tt>@path</tt>
32
+ # to handle the update. For example, to update the +VoiceUrl+ of a Twilio
33
+ # Application you could write:
34
+ #
35
+ # @app.update voice_url: 'http://my.other.app.com/handle_voice'
36
+ #
37
+ # After returning, the object will contain the most recent state of the
38
+ # instance resource, including the newly updated properties.
39
+ def update(params = {})
40
+ raise "Can't update a resource without a REST Client" unless @client
41
+ set_up_properties_from(@client.post(@path, params))
42
+ self
43
+ end
44
+
45
+ ##
46
+ # Refresh the attributes of this instance resource object by fetching it
47
+ # from Twilio. Calling this makes an HTTP GET request to <tt>@path</tt>.
48
+ def refresh
49
+ raise "Can't refresh a resource without a REST Client" unless @client
50
+ @updated = false
51
+ set_up_properties_from(@client.get(@path))
52
+ self
53
+ end
54
+
55
+ ##
56
+ # Delete an instance resource from Twilio. This operation isn't always
57
+ # supported. For instance, you can't delete an SMS. Calling this method
58
+ # makes an HTTP DELETE request to <tt>@path</tt>.
59
+ def delete
60
+ raise "Can't delete a resource without a REST Client" unless @client
61
+ @client.delete @path
62
+ end
63
+
64
+ ##
65
+ # Lazily load attributes of the instance resource by waiting to fetch it
66
+ # until an attempt is made to access an unknown attribute.
67
+ def method_missing(method, *args)
68
+ super if @updated
69
+ set_up_properties_from(@client.get(@path))
70
+ self.send method, *args
71
+ end
72
+
73
+ protected
74
+
75
+ def set_up_properties_from(hash)
76
+ eigenclass = class << self; self; end
77
+ hash.each do |p,v|
78
+ property = detwilify p
79
+ unless ['client', 'updated'].include? property
80
+ eigenclass.send :define_method, property.to_sym, &lambda { v }
81
+ end
82
+ end
83
+ @updated = !hash.keys.empty?
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,100 @@
1
+ module TwilioLookups
2
+ module REST
3
+ class ListResource
4
+ include Utils
5
+
6
+ def initialize(path, client)
7
+ custom_names = {
8
+ 'Activities' => 'Activity',
9
+ 'Addresses' => 'Address',
10
+ 'Countries' => 'Country',
11
+ 'Feedback' => 'FeedbackInstance',
12
+ 'IpAddresses' => 'IpAddress',
13
+ 'Media' => 'MediaInstance',
14
+ }
15
+ @path, @client = path, client
16
+ resource_name = self.class.name.split('::')[-1]
17
+ instance_name = custom_names.fetch(resource_name, resource_name.chop)
18
+
19
+ # The next line grabs the enclosing module. Necessary for resources
20
+ # contained in their own submodule like /SMS/Messages
21
+ parent_module = self.class.to_s.split('::')[-2]
22
+ full_module_path = if parent_module == "REST"
23
+ TwilioLookups::REST
24
+ else
25
+ TwilioLookups::REST.const_get parent_module
26
+ end
27
+
28
+ @instance_class = full_module_path.const_get instance_name
29
+ @list_key, @instance_id_key = detwilify(resource_name), 'sid'
30
+ end
31
+
32
+ def inspect # :nodoc:
33
+ "<#{self.class} @path=#{@path}>"
34
+ end
35
+
36
+ ##
37
+ # Grab a list of this kind of resource and return it as an array. The
38
+ # array includes two special methods +previous_page+ and +next_page+
39
+ # which will return the previous or next page or resources. By default
40
+ # Twilio will only return 50 resources, and the maximum number of
41
+ # resources you can request (using the :page_size param) is 1000.
42
+ #
43
+ # The optional +params+ hash allows you to filter the list returned. The
44
+ # filters for each list resource type are defined by Twilio.
45
+ def list(params={}, full_path=false)
46
+ raise "Can't get a resource list without a REST Client" unless @client
47
+ response = @client.get @path, params, full_path
48
+ resources = response[@list_key]
49
+ path = full_path ? @path.split('.')[0] : @path
50
+ resource_list = resources.map do |resource|
51
+ @instance_class.new "#{path}/#{resource[@instance_id_key]}", @client,
52
+ resource
53
+ end
54
+ # set the +previous_page+ and +next_page+ properties on the array
55
+ client, list_class = @client, self.class
56
+ resource_list.instance_eval do
57
+ eigenclass = class << self; self; end
58
+ eigenclass.send :define_method, :previous_page, &lambda {
59
+ if response['previous_page_uri']
60
+ list_class.new(response['previous_page_uri'], client).list({}, true)
61
+ else
62
+ []
63
+ end
64
+ }
65
+ eigenclass.send :define_method, :next_page, &lambda {
66
+ if response['next_page_uri']
67
+ list_class.new(response['next_page_uri'], client).list({}, true)
68
+ else
69
+ []
70
+ end
71
+ }
72
+ end
73
+ resource_list
74
+ end
75
+
76
+ ##
77
+ # Return an empty instance resource object with the proper path. Note that
78
+ # this will never raise a Twilio::REST::RequestError on 404 since no HTTP
79
+ # request is made. The HTTP request is made when attempting to access an
80
+ # attribute of the returned instance resource object, such as
81
+ # its #date_created or #voice_url attributes.
82
+ def get(sid)
83
+ @instance_class.new "#{@path}/#{sid}", @client
84
+ end
85
+ alias :find :get # for the ActiveRecord lovers
86
+
87
+ ##
88
+ # Return a newly created resource. Some +params+ may be required. Consult
89
+ # the Twilio REST API documentation related to the kind of resource you
90
+ # are attempting to create for details. Calling this method makes an HTTP
91
+ # POST request to <tt>@path</tt> with the given params
92
+ def create(params={})
93
+ raise "Can't create a resource without a REST Client" unless @client
94
+ response = @client.post @path, params
95
+ @instance_class.new "#{@path}/#{response[@instance_id_key]}", @client,
96
+ response
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,17 @@
1
+ module TwilioLookups
2
+ module REST
3
+ module Lookups
4
+ class PhoneNumbers < TwilioLookups::REST::NextGenListResource;
5
+ include TwilioLookups::Util
6
+ include TwilioLookups::REST::Utils
7
+
8
+ def get(number, query={})
9
+ full_path = "#{@path}/#{URI.encode(number)}"
10
+ full_path << "?#{url_encode(twilify(query))}" if !query.empty?
11
+ @instance_class.new full_path, @client
12
+ end
13
+ end
14
+ class PhoneNumber < InstanceResource; end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,99 @@
1
+ require 'twilio-lookups/rest/base_client'
2
+ module TwilioLookups
3
+ module REST
4
+ class LookupsClient < BaseClient
5
+ API_VERSION = 'v1'
6
+
7
+ attr_reader :phone_numbers
8
+
9
+ host 'lookups.twilio.com'
10
+
11
+ ##
12
+ # Instantiate a new HTTP Lookups client to talk to TwilioLookups. The parameters
13
+ # +account_sid+, +auth_token+ and +workspace_sid are required, unless you
14
+ # have configured them already using the block configure syntax, and used
15
+ # to generate the HTTP basic auth header in each request. The +options+
16
+ # parameter is a hash of connection configuration options. the following
17
+ # keys are supported:
18
+ #
19
+ # === <tt>host: 'lookups.twilio.com'</tt>
20
+ #
21
+ # The domain to which you'd like the client to make HTTP requests. Useful
22
+ # for testing. Defaults to 'lookups.twilio.com'.
23
+ #
24
+ # === <tt>port: 443</tt>
25
+ #
26
+ # The port on which to connect to the above domain. Defaults to 443 and
27
+ # should be left that way except in testing environments.
28
+ #
29
+ # === <tt>use_ssl: true</tt>
30
+ #
31
+ # Declare whether ssl should be used for connections to the above domain.
32
+ # Defaults to true and should be left alone except when testing.
33
+ #
34
+ # === <tt>ssl_verify_peer: true</tt>
35
+ #
36
+ # Declare whether to verify the host's ssl cert when setting up the
37
+ # connection to the above domain. Defaults to true, but can be turned off
38
+ # to avoid ssl certificate verification failures in environments without
39
+ # the necessary ca certificates.
40
+ #
41
+ # === <tt>ssl_ca_file: '/path/to/ca/file'</tt>
42
+ #
43
+ # Specify the path to the certificate authority bundle you'd like to use
44
+ # to verify TwilioLookups's SSL certificate on each request. If not specified, a
45
+ # certificate bundle extraced from Firefox is packaged with the gem and
46
+ # used by default.
47
+ #
48
+ # === <tt>timeout: 30</tt>
49
+ #
50
+ # Set the time in seconds to wait before timing out the HTTP request.
51
+ # Defaults to 30 seconds. If you aren't fetching giant pages of call or
52
+ # SMS logs you can safely decrease this to something like 3 seconds or
53
+ # lower. In paricular if you are sending SMS you can set this to 1 second
54
+ # or less and swallow the exception if you don't care about the response.
55
+ #
56
+ # === <tt>proxy_addr: 'proxy.host.domain'</tt>
57
+ #
58
+ # The domain of a proxy through which you'd like the client to make HTTP
59
+ # requests. Defaults to nil.
60
+ #
61
+ # === <tt>proxy_port: 3128</tt>
62
+ #
63
+ # The port on which to connect to the above proxy. Defaults to nil.
64
+ #
65
+ # === <tt>proxy_user: 'username'</tt>
66
+ #
67
+ # The user name to use for authentication with the proxy. Defaults to nil.
68
+ #
69
+ # === <tt>proxy_pass: 'password'</tt>
70
+ #
71
+ # The password to use for authentication with the proxy. Defaults to nil.
72
+ #
73
+ # === <tt>retry_limit: 1</tt>
74
+ #
75
+ # The number of times to retry a request that has failed before throwing
76
+ # an exception. Defaults to one.
77
+ def inspect # :nodoc:
78
+ "<TwilioLookups::REST::LookupsClient @account_sid=#{@account_sid}>"
79
+ end
80
+
81
+ protected
82
+
83
+ ##
84
+ # Set up +phone_numbers+ attribute.
85
+ def set_up_subresources # :doc:
86
+ @phone_numbers = TwilioLookups::REST::Lookups::PhoneNumbers.new "/#{API_VERSION}/PhoneNumbers", self
87
+ end
88
+
89
+ ##
90
+ # Builds up full request path
91
+ def build_full_path(path, params, method)
92
+ path = path.dup
93
+ path << "?#{url_encode(params)}" if method == :get && !params.empty?
94
+ path
95
+ end
96
+
97
+ end
98
+ end
99
+ end