twilio-lookups 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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