twilio-ruby 3.11.5 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +13 -5
  2. data/.gitignore +1 -1
  3. data/.travis.yml +20 -9
  4. data/AUTHORS.md +32 -25
  5. data/CHANGES.md +203 -0
  6. data/Gemfile +8 -1
  7. data/{LICENSE → LICENSE.md} +4 -2
  8. data/Makefile +1 -2
  9. data/README.md +70 -39
  10. data/Rakefile +8 -10
  11. data/docs/faq.rst +3 -3
  12. data/docs/getting-started.rst +17 -12
  13. data/docs/index.rst +15 -0
  14. data/docs/usage/accounts.rst +6 -6
  15. data/docs/usage/addresses.rst +101 -0
  16. data/docs/usage/applications.rst +10 -10
  17. data/docs/usage/basics.rst +17 -4
  18. data/docs/usage/caller-ids.rst +4 -2
  19. data/docs/usage/conferences.rst +11 -11
  20. data/docs/usage/errors.rst +7 -7
  21. data/docs/usage/messages.rst +48 -20
  22. data/docs/usage/notifications.rst +6 -4
  23. data/docs/usage/phone-calls.rst +33 -14
  24. data/docs/usage/phone-numbers.rst +45 -31
  25. data/docs/usage/queues.rst +8 -8
  26. data/docs/usage/recordings.rst +12 -10
  27. data/docs/usage/sip.rst +15 -14
  28. data/docs/usage/taskrouter-tokens.rst +98 -0
  29. data/docs/usage/taskrouter.rst +226 -0
  30. data/docs/usage/token-generation.rst +19 -4
  31. data/docs/usage/transcriptions.rst +3 -2
  32. data/docs/usage/twiml.rst +7 -7
  33. data/docs/usage/validation.rst +39 -3
  34. data/examples/examples.rb +44 -20
  35. data/examples/print-call-log.rb +1 -1
  36. data/lib/rack/twilio_webhook_authentication.rb +47 -0
  37. data/lib/twilio-ruby/rest/accounts.rb +2 -1
  38. data/lib/twilio-ruby/rest/addresses/dependent_phone_numbers.rb +6 -0
  39. data/lib/twilio-ruby/rest/addresses.rb +12 -0
  40. data/lib/twilio-ruby/rest/base_client.rb +127 -0
  41. data/lib/twilio-ruby/rest/call_feedback.rb +28 -0
  42. data/lib/twilio-ruby/rest/call_feedback_summary.rb +13 -0
  43. data/lib/twilio-ruby/rest/calls.rb +10 -5
  44. data/lib/twilio-ruby/rest/client.rb +44 -109
  45. data/lib/twilio-ruby/rest/conferences/participants.rb +2 -2
  46. data/lib/twilio-ruby/rest/incoming_phone_numbers.rb +1 -1
  47. data/lib/twilio-ruby/rest/instance_resource.rb +2 -16
  48. data/lib/twilio-ruby/rest/list_resource.rb +20 -30
  49. data/lib/twilio-ruby/rest/lookups/phone_numbers.rb +17 -0
  50. data/lib/twilio-ruby/rest/lookups_client.rb +99 -0
  51. data/lib/twilio-ruby/rest/messages.rb +5 -0
  52. data/lib/twilio-ruby/rest/next_gen_list_resource.rb +36 -0
  53. data/lib/twilio-ruby/rest/outgoing_caller_ids.rb +1 -1
  54. data/lib/twilio-ruby/rest/queues/members.rb +1 -1
  55. data/lib/twilio-ruby/rest/sip.rb +1 -3
  56. data/lib/twilio-ruby/rest/sms/messages.rb +23 -0
  57. data/lib/twilio-ruby/rest/task_router/activities.rb +8 -0
  58. data/lib/twilio-ruby/rest/task_router/events.rb +8 -0
  59. data/lib/twilio-ruby/rest/task_router/reservations.rb +8 -0
  60. data/lib/twilio-ruby/rest/task_router/statistics.rb +26 -0
  61. data/lib/twilio-ruby/rest/task_router/task_queues.rb +17 -0
  62. data/lib/twilio-ruby/rest/task_router/task_queues_statistics.rb +15 -0
  63. data/lib/twilio-ruby/rest/task_router/tasks.rb +15 -0
  64. data/lib/twilio-ruby/rest/task_router/workers.rb +13 -0
  65. data/lib/twilio-ruby/rest/task_router/workers_statistics.rb +8 -0
  66. data/lib/twilio-ruby/rest/task_router/workflow_statistics.rb +7 -0
  67. data/lib/twilio-ruby/rest/task_router/workflows.rb +11 -0
  68. data/lib/twilio-ruby/rest/task_router/workspace_statistics.rb +7 -0
  69. data/lib/twilio-ruby/rest/task_router/workspaces.rb +17 -0
  70. data/lib/twilio-ruby/rest/task_router_client.rb +176 -0
  71. data/lib/twilio-ruby/rest/tokens.rb +7 -0
  72. data/lib/twilio-ruby/rest/usage/records.rb +2 -2
  73. data/lib/twilio-ruby/rest/utils.rb +35 -11
  74. data/lib/twilio-ruby/task_router/capability.rb +87 -0
  75. data/lib/twilio-ruby/task_router.rb +0 -0
  76. data/lib/twilio-ruby/twiml/response.rb +1 -0
  77. data/lib/twilio-ruby/util/capability.rb +10 -7
  78. data/lib/twilio-ruby/util/client_config.rb +29 -0
  79. data/lib/twilio-ruby/util/configuration.rb +7 -0
  80. data/lib/twilio-ruby/util/request_validator.rb +18 -3
  81. data/lib/twilio-ruby/version.rb +1 -1
  82. data/lib/twilio-ruby.rb +48 -0
  83. data/spec/rack/twilio_webhook_authentication_spec.rb +110 -0
  84. data/spec/rest/account_spec.rb +51 -20
  85. data/spec/rest/address_spec.rb +11 -0
  86. data/spec/rest/call_feedback_spec.rb +12 -0
  87. data/spec/rest/call_feedback_summary_spec.rb +9 -0
  88. data/spec/rest/call_spec.rb +8 -4
  89. data/spec/rest/client_spec.rb +209 -51
  90. data/spec/rest/conference_spec.rb +4 -2
  91. data/spec/rest/instance_resource_spec.rb +4 -4
  92. data/spec/rest/lookups/phone_number_spec.rb +8 -0
  93. data/spec/rest/message_spec.rb +2 -2
  94. data/spec/rest/numbers_spec.rb +25 -13
  95. data/spec/rest/queue_spec.rb +4 -2
  96. data/spec/rest/recording_spec.rb +4 -2
  97. data/spec/rest/sms/message_spec.rb +37 -0
  98. data/spec/rest/sms/messages_spec.rb +31 -0
  99. data/spec/rest/task_router/reservation_spec.rb +9 -0
  100. data/spec/rest/task_router/task_queue_spec.rb +9 -0
  101. data/spec/rest/token_spec.rb +7 -0
  102. data/spec/rest/utils_spec.rb +45 -0
  103. data/spec/spec_helper.rb +12 -3
  104. data/spec/support/fakeweb.rb +2 -0
  105. data/spec/task_router_spec.rb +114 -0
  106. data/spec/twilio_spec.rb +15 -0
  107. data/spec/util/capability_spec.rb +167 -118
  108. data/spec/util/client_config_spec.rb +21 -0
  109. data/spec/util/configuration_spec.rb +15 -0
  110. data/spec/util/request_validator_spec.rb +31 -3
  111. data/spec/util/url_encode_spec.rb +2 -2
  112. data/twilio-ruby.gemspec +28 -27
  113. metadata +93 -71
  114. data/CHANGES +0 -47
@@ -0,0 +1,26 @@
1
+ module Twilio
2
+ module REST
3
+ module TaskRouter
4
+ module Statistics
5
+ def statistics(args={})
6
+ path = "#{@path}/Statistics"
7
+ response = @client.get(path, args, true)
8
+ statistics_class.new(path, @client, response)
9
+ end
10
+
11
+ private
12
+
13
+ ##
14
+ # Gets the class for the statistics of the current class.
15
+ # Should just be Object.const_get("#{self.class.to_s}Statistics") but
16
+ # Ruby 1.9.3 did not agree. Can be updated if support is dropped.
17
+ def statistics_class
18
+ current_class = self.class.to_s.split('::').last
19
+ statistics_class = Twilio::REST::TaskRouter.const_get(
20
+ "#{current_class}Statistics"
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ module Twilio
2
+ module REST
3
+ module TaskRouter
4
+ class TaskQueues < Twilio::REST::NextGenListResource
5
+ def statistics(args={})
6
+ path = "#{@path}/Statistics"
7
+ stats = Twilio::REST::TaskRouter::TaskQueuesStatistics.new path, @client
8
+ stats.list args, true
9
+ end
10
+ end
11
+
12
+ class TaskQueue < InstanceResource
13
+ include Statistics
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module Twilio
2
+ module REST
3
+ module TaskRouter
4
+ class TaskQueuesStatistics < Twilio::REST::NextGenListResource
5
+ def initialize(path, client)
6
+ @path, @client = path, client
7
+ @instance_class = Twilio::REST::TaskRouter::TaskQueueStatistics
8
+ @list_key, @instance_id_key = 'task_queues_statistics', 'task_queue_sid'
9
+ end
10
+ end
11
+
12
+ class TaskQueueStatistics < InstanceResource; end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Twilio
2
+ module REST
3
+ module TaskRouter
4
+ class Tasks < Twilio::REST::NextGenListResource; end
5
+
6
+ class Task < InstanceResource
7
+ def initialize(path, client, params={})
8
+ super path, client, params
9
+ @submodule = :TaskRouter
10
+ resource :reservations
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ module Twilio
2
+ module REST
3
+ module TaskRouter
4
+ class Workers < Twilio::REST::NextGenListResource
5
+ include Twilio::REST::TaskRouter::Statistics
6
+ end
7
+
8
+ class Worker < InstanceResource
9
+ include Twilio::REST::TaskRouter::Statistics
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ module Twilio
2
+ module REST
3
+ module TaskRouter
4
+ class WorkersStatistics < Twilio::REST::InstanceResource; end
5
+ class WorkerStatistics < InstanceResource; end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ module Twilio
2
+ module REST
3
+ module TaskRouter
4
+ class WorkflowStatistics < InstanceResource; end
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ module Twilio
2
+ module REST
3
+ module TaskRouter
4
+ class Workflows < Twilio::REST::NextGenListResource; end
5
+
6
+ class Workflow < InstanceResource
7
+ include Twilio::REST::TaskRouter::Statistics
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Twilio
2
+ module REST
3
+ module TaskRouter
4
+ class WorkspaceStatistics < InstanceResource; end
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,17 @@
1
+ module Twilio
2
+ module REST
3
+ module TaskRouter
4
+ class Workspaces < Twilio::REST::NextGenListResource; end
5
+
6
+ class Workspace < InstanceResource
7
+ include Twilio::REST::TaskRouter::Statistics
8
+
9
+ def initialize(path, client, params={})
10
+ super path, client, params
11
+ @submodule = :TaskRouter
12
+ resource :activities, :events, :task_queues, :tasks, :workers, :workflows
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,176 @@
1
+ require 'twilio-ruby/rest/base_client'
2
+ module Twilio
3
+ module REST
4
+ class TaskRouterClient < BaseClient
5
+ API_VERSION = 'v1'
6
+
7
+ attr_reader :workspace, :workspace_sid, :workspaces
8
+
9
+ host 'taskrouter.twilio.com'
10
+
11
+ ##
12
+ # Instantiate a new HTTP TaskRouter client to talk to Twilio. 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: 'taskrouter.twilio.com'</tt>
20
+ #
21
+ # The domain to which you'd like the client to make HTTP requests. Useful
22
+ # for testing. Defaults to 'api.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 Twilio'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 initialize(*args)
78
+ @workspace_sid = args[2]
79
+ if @workspace_sid.nil?
80
+ raise ArgumentError, 'Workspace SID is required'
81
+ end
82
+ super(*args)
83
+ end
84
+
85
+ def inspect # :nodoc:
86
+ "<Twilio::REST::TaskRouterClient @account_sid=#{@account_sid}>"
87
+ end
88
+
89
+ ##
90
+ # Delegate workspace methods from the client. This saves having to call
91
+ # <tt>client.workspace</tt> every time for resources on the default
92
+ # workspace.
93
+ def method_missing(method_name, *args, &block)
94
+ if workspace.respond_to?(method_name)
95
+ workspace.send(method_name, *args, &block)
96
+ else
97
+ super
98
+ end
99
+ end
100
+
101
+ def respond_to?(method_name, include_private=false)
102
+ if workspace.respond_to?(method_name, include_private)
103
+ true
104
+ else
105
+ super
106
+ end
107
+ end
108
+
109
+ ##
110
+ # Get statistics of a task queue.
111
+ def task_queue_statistics(task_queue_sid, *args) # :doc:
112
+ warn "[DEPRECATED] task_queue_statistics is deprecated. " \
113
+ "Please call client.task_queue.get(sid).statistics."
114
+ task_queues.get(task_queue_sid).statistics
115
+ end
116
+
117
+ ##
118
+ # Get statistics of task queues.
119
+ def task_queues_statistics(*args) # :doc:
120
+ warn "[DEPRECATED] task_queues_statistics is deprecated. " \
121
+ "Please call client.task_queues.statistics."
122
+ task_queues.statistics
123
+ end
124
+
125
+ ##
126
+ # Get statistics of a worker.
127
+ def worker_statistics(worker_sid, *args) # :doc:
128
+ warn "[DEPRECATED] worker_statistics is deprecated. " \
129
+ "Please call client.worker.get(sid).statistics."
130
+ workers.get(worker_sid).statistics
131
+ end
132
+
133
+ ##
134
+ # Get statistics of workers.
135
+ def workers_statistics(*args) # :doc:
136
+ warn "[DEPRECATED] workers_statistics is deprecated. " \
137
+ "Please call client.workers.statistics."
138
+ workers.statistics
139
+ end
140
+
141
+ ##
142
+ # Get statistics of a workflow.
143
+ def workflow_statistics(workflow_sid, *args) # :doc:
144
+ warn "[DEPRECATED] workflow_statistics is deprecated. " \
145
+ "Please call client.workflow.get(sid).statistics."
146
+ workflows.get(workflow_sid).statistics
147
+ end
148
+
149
+ ##
150
+ # Get statistics of a workspace.
151
+ def workspace_statistics(*args) # :doc:
152
+ warn "[DEPRECATED] worker_statistics is deprecated. " \
153
+ "Please call client.workspace.statistics."
154
+ workspace.statistics
155
+ end
156
+
157
+ protected
158
+
159
+ ##
160
+ # Set up +workspace+ and +workspaces+ attributes.
161
+ def set_up_subresources # :doc:
162
+ @workspaces = Twilio::REST::TaskRouter::Workspaces.new "/#{API_VERSION}/Workspaces", self
163
+ @workspace = @workspaces.get @workspace_sid
164
+ end
165
+
166
+ ##
167
+ # Builds up full request path
168
+ def build_full_path(path, params, method)
169
+ path = path.dup
170
+ path << "?#{url_encode(params)}" if method == :get && !params.empty?
171
+ path
172
+ end
173
+
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,7 @@
1
+ module Twilio
2
+ module REST
3
+ class Tokens < ListResource; end
4
+
5
+ class Token < InstanceResource; end
6
+ end
7
+ end
@@ -2,8 +2,8 @@ module Twilio
2
2
  module REST
3
3
  class Records < ListResource
4
4
 
5
- SUBRESOURCES = [:daily, :monthly, :yearly, :all_time, :today, :yesterday,
6
- :this_month, :last_month]
5
+ SUBRESOURCES = [:daily, :monthly, :yearly, :all_time, :today,
6
+ :yesterday, :this_month, :last_month]
7
7
 
8
8
  def initialize(path, client)
9
9
  super
@@ -3,23 +3,47 @@ module Twilio
3
3
  module Utils
4
4
 
5
5
  def twilify(something)
6
- if something.is_a? Hash
7
- Hash[*something.to_a.map! {|a| [twilify(a[0]).to_sym, a[1]]}.flatten(1)]
8
- else
9
- something.to_s.split('_').map! do |s|
10
- [s[0,1].capitalize, s[1..-1]].join
11
- end.join
12
- end
6
+ return key_map(something, :twilify) if something.is_a? Hash
7
+ string = something.to_s
8
+ string.split('_').map do |string_part|
9
+ string_part[0,1].capitalize + string_part[1..-1]
10
+ end.join
13
11
  end
14
12
 
15
13
  def detwilify(something)
16
- if something.is_a? Hash
17
- Hash[*something.to_a.map! {|pair| [detwilify(pair[0]).to_sym, pair[1]]}.flatten]
18
- else
19
- something.to_s.gsub(/[A-Z][a-z]*/) {|s| "_#{s.downcase}"}.gsub(/^_/, '')
14
+ return key_map(something, :detwilify) if something.is_a? Hash
15
+ string = something.to_s
16
+ string = string[0,1].downcase + string[1..-1]
17
+ string.gsub(/[A-Z][a-z]*/) { |s| "_#{s.downcase}" }
18
+ end
19
+
20
+ protected
21
+
22
+ def resource(*resources)
23
+ custom_resource_names = { sms: 'SMS', sip: 'SIP' }
24
+ resources.each do |r|
25
+ resource = twilify r
26
+ relative_path = custom_resource_names.fetch(r, resource)
27
+ path = "#{@path}/#{relative_path}"
28
+ enclosing_module = if @submodule == nil
29
+ Twilio::REST
30
+ else
31
+ Twilio::REST.const_get(@submodule)
32
+ end
33
+ resource_class = enclosing_module.const_get resource
34
+ instance_variable_set("@#{r}", resource_class.new(path, @client))
20
35
  end
36
+ self.class.instance_eval { attr_reader *resources }
21
37
  end
22
38
 
39
+ private
40
+
41
+ def key_map(something, method)
42
+ something = something.to_a.flat_map do |pair|
43
+ [send(method, pair[0]).to_sym, pair[1]]
44
+ end
45
+ Hash[*something]
46
+ end
23
47
  end
24
48
  end
25
49
  end
@@ -0,0 +1,87 @@
1
+ module Twilio
2
+ module TaskRouter
3
+ class Capability
4
+
5
+ TASK_ROUTER_BASE_URL = 'https://taskrouter.twilio.com'
6
+ TASK_ROUTER_VERSION = 'v1'
7
+ TASK_ROUTER_WEBSOCKET_BASE_URL = 'https://event-bridge.twilio.com/v1/wschannels'
8
+
9
+ REQUIRED = {required: true}
10
+ OPTIONAL = {required: false}
11
+
12
+ def initialize(account_sid, auth_token, workspace_sid, worker_sid)
13
+ @account_sid = account_sid
14
+ @auth_token = auth_token
15
+ @workspace_sid = workspace_sid
16
+ @worker_sid = worker_sid
17
+ @policies = []
18
+ allow_websocket_requests
19
+ allow_activity_list_fetch
20
+ end
21
+
22
+ def workspace_url
23
+ "#{TASK_ROUTER_BASE_URL}/#{TASK_ROUTER_VERSION}/Workspaces/#{@workspace_sid}"
24
+ end
25
+
26
+ def worker_url
27
+ "#{workspace_url}/Workers/#{@worker_sid}"
28
+ end
29
+
30
+ def allow_worker_activity_updates
31
+ add_policy(worker_url, "POST", nil, {ActivitySid: REQUIRED})
32
+ end
33
+
34
+ def allow_worker_fetch_attributes
35
+ add_policy(worker_url, "GET")
36
+ end
37
+
38
+ def allow_task_reservation_updates
39
+ task_url = "#{workspace_url}/Tasks/**"
40
+ add_policy(task_url, "POST", nil, {ReservationStatus: REQUIRED})
41
+ end
42
+
43
+ def add_policy(url, method, query_filters = nil, post_filters = nil, allowed = true)
44
+ policy = {
45
+ url: url,
46
+ method: method,
47
+ query_filter: query_filters || {},
48
+ post_filter: post_filters || {},
49
+ allow: allowed
50
+ }
51
+
52
+ @policies.push(policy)
53
+ end
54
+
55
+ def generate_token(ttl = 3600)
56
+ payload = {
57
+ iss: @account_sid,
58
+ exp: (Time.now.to_i + ttl),
59
+ version: @version,
60
+ friendly_name: @worker_sid,
61
+ policies: @policies,
62
+ account_sid: @account_sid,
63
+ worker_sid: @worker_sid,
64
+ channel: @worker_sid,
65
+ workspace_sid: @workspace_sid
66
+ }
67
+
68
+ JWT.encode payload, @auth_token
69
+ end
70
+
71
+ protected
72
+
73
+ def allow_websocket_requests
74
+ worker_url = "#{TASK_ROUTER_WEBSOCKET_BASE_URL}/#{@account_sid}/#{@worker_sid}"
75
+ ['GET', 'POST'].each do |meth|
76
+ add_policy(worker_url, meth)
77
+ end
78
+ end
79
+
80
+ def allow_activity_list_fetch
81
+ url = "#{workspace_url}/Activities"
82
+ add_policy(url, 'GET')
83
+ end
84
+
85
+ end
86
+ end
87
+ end
File without changes
@@ -3,6 +3,7 @@ module Twilio
3
3
  class Response
4
4
 
5
5
  attr_reader :text
6
+ alias_method :to_xml , :text
6
7
 
7
8
  def initialize(&block)
8
9
  xml = Builder::XmlMarkup.new
@@ -4,28 +4,31 @@ module Twilio
4
4
 
5
5
  include Twilio::Util
6
6
 
7
- def initialize(account_sid, auth_token)
8
- @account_sid = account_sid
9
- @auth_token = auth_token
7
+ def initialize(account_sid = nil, auth_token = nil)
8
+ @account_sid = account_sid || Twilio.account_sid
9
+ @auth_token = auth_token || Twilio.auth_token
10
+ if @account_sid.nil? || @auth_token.nil?
11
+ raise ArgumentError, 'Account SID and auth token are required'
12
+ end
10
13
  @capabilities = []
11
14
  end
12
15
 
13
16
  def allow_client_incoming(client_name)
14
17
  @client_name = client_name # stash for use in outgoing
15
- scope_params = {'clientName' => client_name}
18
+ scope_params = { 'clientName' => client_name }
16
19
  @capabilities << scope_uri_for('client', 'incoming', scope_params)
17
20
  end
18
21
 
19
22
  def allow_client_outgoing(app_sid, params = {})
20
23
  @allow_client_outgoing = true
21
- @outgoing_scope_params = {'appSid' => app_sid}
24
+ @outgoing_scope_params = { 'appSid' => app_sid }
22
25
  unless params.empty?
23
26
  @outgoing_scope_params['appParams'] = url_encode params
24
27
  end
25
28
  end
26
29
 
27
30
  def allow_event_stream(filters = {})
28
- scope_params = {'path' => '/2010-04-01/Events'}
31
+ scope_params = { 'path' => '/2010-04-01/Events' }
29
32
  scope_params['params'] = filters unless filters.empty?
30
33
  @capabilities << scope_uri_for('stream', 'subscribe', scope_params)
31
34
  end
@@ -42,7 +45,7 @@ module Twilio
42
45
  # build the outgoing scope lazily so that we can use @client_name
43
46
  if @allow_client_outgoing
44
47
  params = @outgoing_scope_params
45
- params.merge!({'clientName' => @client_name}) if @client_name
48
+ params.merge!('clientName' => @client_name) if @client_name
46
49
  capabilities << scope_uri_for('client', 'outgoing', params)
47
50
  end
48
51
 
@@ -0,0 +1,29 @@
1
+ module Twilio
2
+ module Util
3
+ class ClientConfig
4
+ DEFAULTS = {
5
+ host: 'api.twilio.com',
6
+ port: 443,
7
+ use_ssl: true,
8
+ ssl_verify_peer: true,
9
+ ssl_ca_file: File.dirname(__FILE__) + '/../../../conf/cacert.pem',
10
+ timeout: 30,
11
+ proxy_addr: nil,
12
+ proxy_port: nil,
13
+ proxy_user: nil,
14
+ proxy_pass: nil,
15
+ retry_limit: 1
16
+ }
17
+
18
+ DEFAULTS.each_key do |attribute|
19
+ attr_accessor attribute
20
+ end
21
+
22
+ def initialize(opts={})
23
+ DEFAULTS.each do |attribute, value|
24
+ send("#{attribute}=".to_sym, opts.fetch(attribute, value))
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ module Twilio
2
+ module Util
3
+ class Configuration
4
+ attr_accessor :account_sid, :auth_token
5
+ end
6
+ end
7
+ end
@@ -2,13 +2,14 @@ module Twilio
2
2
  module Util
3
3
  class RequestValidator
4
4
 
5
- def initialize(auth_token)
6
- @auth_token = auth_token
5
+ def initialize(auth_token = nil)
6
+ @auth_token = auth_token || Twilio.auth_token
7
+ raise ArgumentError, 'Auth token is required' if @auth_token.nil?
7
8
  end
8
9
 
9
10
  def validate(url, params, signature)
10
11
  expected = build_signature_for url, params
11
- expected == signature
12
+ secure_compare(expected, signature)
12
13
  end
13
14
 
14
15
  def build_signature_for(url, params)
@@ -17,6 +18,20 @@ module Twilio
17
18
  Base64.encode64(OpenSSL::HMAC.digest(digest, @auth_token, data)).strip
18
19
  end
19
20
 
21
+ private
22
+
23
+ # Compares two strings in constant time to avoid timing attacks.
24
+ # Borrowed from ActiveSupport::MessageVerifier.
25
+ # https://github.com/rails/rails/blob/master/activesupport/lib/active_support/message_verifier.rb
26
+ def secure_compare(a, b)
27
+ return false unless a.bytesize == b.bytesize
28
+
29
+ l = a.unpack("C#{a.bytesize}")
30
+
31
+ res = 0
32
+ b.each_byte { |byte| res |= byte ^ l.shift }
33
+ res == 0
34
+ end
20
35
  end
21
36
  end
22
37
  end
@@ -1,3 +1,3 @@
1
1
  module Twilio
2
- VERSION = '3.11.5'
2
+ VERSION = '4.0.0'
3
3
  end