├── .gitignore ├── .travis.yml ├── Gemfile ├── README.md ├── lib ├── railtie.rb ├── twilio-rb.rb ├── twilio.rb └── twilio │ ├── account.rb │ ├── application.rb │ ├── association_proxy.rb │ ├── associations.rb │ ├── authorized_connect_app.rb │ ├── available_phone_number.rb │ ├── call.rb │ ├── capability.rb │ ├── conference.rb │ ├── config.rb │ ├── connect_app.rb │ ├── deletable.rb │ ├── finder.rb │ ├── incoming_phone_number.rb │ ├── notification.rb │ ├── outgoing_caller_id.rb │ ├── participant.rb │ ├── persistable.rb │ ├── queue.rb │ ├── recording.rb │ ├── request_filter.rb │ ├── resource.rb │ ├── sandbox.rb │ ├── short_code.rb │ ├── sms.rb │ ├── transcription.rb │ └── twiml.rb ├── rakefile ├── spec ├── account_spec.rb ├── application_spec.rb ├── authorized_connect_app_spec.rb ├── available_phone_number_spec.rb ├── call_spec.rb ├── capability_spec.rb ├── conference_spec.rb ├── config_spec.rb ├── connect_app_spec.rb ├── incoming_phone_number_spec.rb ├── notification_spec.rb ├── outgoing_caller_id_spec.rb ├── participant_spec.rb ├── queue_spec.rb ├── recording_spec.rb ├── request_filter_spec.rb ├── sandbox_spec.rb ├── short_code_spec.rb ├── sms_spec.rb ├── spec_helper.rb ├── support │ └── responses │ │ ├── account.json │ │ ├── api_error.json │ │ ├── application.json │ │ ├── authorized_connect_app.json │ │ ├── available_local_phone_numbers.json │ │ ├── available_toll_free_phone_numbers.json │ │ ├── call_cancelled.json │ │ ├── call_completed.json │ │ ├── call_created.json │ │ ├── call_url_modified.json │ │ ├── caller_id.json │ │ ├── conference.json │ │ ├── connect_account.json │ │ ├── connect_app.json │ │ ├── connect_application.json │ │ ├── connect_call_created.json │ │ ├── connect_caller_id.json │ │ ├── connect_conference.json │ │ ├── connect_incoming_phone_number.json │ │ ├── connect_notification.json │ │ ├── connect_recording.json │ │ ├── connect_sms_created.json │ │ ├── connect_transcription.json │ │ ├── incoming_phone_number.json │ │ ├── list_accounts.json │ │ ├── list_applications.json │ │ ├── list_authorized_connect_apps.json │ │ ├── list_caller_ids.json │ │ ├── list_calls.json │ │ ├── list_conferences.json │ │ ├── list_connect_applications.json │ │ ├── list_connect_apps.json │ │ ├── list_connect_caller_ids.json │ │ ├── list_connect_calls.json │ │ ├── list_connect_conferences.json │ │ ├── list_connect_incoming_phone_numbers.json │ │ ├── list_connect_messages.json │ │ ├── list_connect_notifications.json │ │ ├── list_connect_recordings.json │ │ ├── list_connect_transcriptions.json │ │ ├── list_incoming_phone_numbers.json │ │ ├── list_messages.json │ │ ├── list_notifications.json │ │ ├── list_participants.json │ │ ├── list_queues.json │ │ ├── list_recordings.json │ │ ├── list_short_codes.json │ │ ├── list_transcriptions.json │ │ ├── muted_participant.json │ │ ├── notification.json │ │ ├── queue.json │ │ ├── recording.json │ │ ├── sandbox.json │ │ ├── short_code.json │ │ ├── show_connect_participant.json │ │ ├── show_participant.json │ │ ├── sms_created.json │ │ └── transcription.json ├── transcription_spec.rb └── twiml_spec.rb └── twilio-rb.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | pkg/* 3 | *.swo 4 | *.swp 5 | *.swn 6 | Gemfile.lock 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - "1.9.3" 4 | - "2.0.0" 5 | - ruby-head 6 | 7 | matrix: 8 | allow_failures: 9 | - rvm: ruby-head 10 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | -------------------------------------------------------------------------------- /lib/railtie.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class Railtie < Rails::Railtie 3 | initializer 'twilio.initialize' do |app| 4 | module ActionView 5 | class Template 6 | module Handlers 7 | class TwiML 8 | class_attribute :default_format 9 | self.default_format = 'text/xml' 10 | 11 | def compile(template) 12 | <<-EOS 13 | controller.content_type = 'text/xml' 14 | Twilio::TwiML.build { |res| #{template.source} } 15 | EOS 16 | end 17 | 18 | def self.call(template) 19 | new.compile(template) 20 | end 21 | end 22 | end 23 | end 24 | end 25 | 26 | ActionController::Base.class_eval { before_filter Twilio::RequestFilter } 27 | 28 | ::ActionView::Template.register_template_handler(:voice, ActionView::Template::Handlers::TwiML) 29 | ::Mime::Type.register 'text/xml', :voice 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/twilio-rb.rb: -------------------------------------------------------------------------------- 1 | %w.each { |lib| require lib } 2 | %w.each { |lib| require File.join(File.dirname(__FILE__), 'twilio', "#{lib}.rb") } 3 | 4 | module Twilio 5 | API_ENDPOINT = 'https://api.twilio.com/2010-04-01' 6 | APIError = Class.new StandardError 7 | ConfigurationError = Class.new StandardError 8 | InvalidStateError = Class.new StandardError 9 | end 10 | 11 | Dir[File.join(File.dirname(__FILE__), 'twilio', '*.rb')].each { |lib| require lib } 12 | 13 | require File.join(File.dirname(__FILE__), 'railtie.rb') if Object.const_defined?(:Rails) && Rails::VERSION::MAJOR >= 3 14 | -------------------------------------------------------------------------------- /lib/twilio.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) +'/twilio-rb.rb' 2 | warn 'require "twilio" is deprecated and will be removed in a future release. Use require "twilio-rb" instead.' 3 | 4 | -------------------------------------------------------------------------------- /lib/twilio/account.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class Account 3 | include Twilio::Resource 4 | include Twilio::Persistable 5 | extend Twilio::Associations 6 | extend Twilio::Finder 7 | 8 | mutable_attributes :friendly_name, :status 9 | 10 | has_many :calls, :sms, :recordings, :conferences, :incoming_phone_numbers, 11 | :notifications, :outgoing_caller_ids, :transcriptions 12 | 13 | class << self 14 | private 15 | def resource_path(account_sid) 16 | "/Accounts" 17 | end 18 | end 19 | 20 | end 21 | end 22 | 23 | -------------------------------------------------------------------------------- /lib/twilio/application.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class Application 3 | extend Twilio::Finder 4 | include Twilio::Resource 5 | include Twilio::Persistable 6 | include Twilio::Deletable 7 | 8 | mutable_attributes :friendly_name, :api_version, :voice_url, :voice_method, :voice_fallback_url, 9 | :voice_fallback_method, :status_callback, :status_callback_method, :sms_url, :sms_method, 10 | :sms_fallback_url, :sms_fallback_method 11 | 12 | end 13 | end 14 | 15 | -------------------------------------------------------------------------------- /lib/twilio/association_proxy.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/string' 2 | 3 | module Twilio 4 | class AssociationProxy 5 | instance_methods.each { |meth| undef_method meth unless meth.to_s =~ /^__/ || meth.to_s == 'object_id' } 6 | def initialize(delegator, target) 7 | @delegator, @target = delegator, target 8 | @delegator_name = @delegator.class.name.demodulize.downcase 9 | end 10 | 11 | def inspect 12 | @target.all :"#{@delegator_name}_sid" => @delegator.sid 13 | end 14 | 15 | def method_missing(meth, *args, &blk) 16 | options = args.empty? ? args.<<({})[-1] : args[-1] 17 | options.update :"#{@delegator_name}_sid" => @delegator.sid 18 | if @delegator[:connect_app_sid] 19 | options.update :connect => true, :account_sid => (@delegator[:account_sid] || @delegator[:sid]) 20 | end 21 | @target.__send__ meth, *args, &blk 22 | end 23 | end 24 | end 25 | 26 | -------------------------------------------------------------------------------- /lib/twilio/associations.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/string' 2 | 3 | module Twilio 4 | module Associations 5 | def has_many(*collection) 6 | collection.each do |objects| 7 | define_method(objects) do 8 | klass = Twilio.const_get objects.to_s.singularize.camelize 9 | Twilio::AssociationProxy.new self, klass 10 | end 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/twilio/authorized_connect_app.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class AuthorizedConnectApp 3 | include Twilio::Resource 4 | extend Twilio::Finder 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/twilio/available_phone_number.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class AvailablePhoneNumber 3 | include Twilio::Resource 4 | extend Twilio::Finder 5 | 6 | class << self 7 | def all(opts={}) 8 | opts = Hash[opts.map { |k,v| [k.to_s.camelize, v]}] 9 | country_code = opts['IsoCountryCode'] ? opts.delete('IsoCountryCode') : 'US' 10 | number_type = opts.delete('TollFree') ? 'TollFree' : 'Local' 11 | params = { :query => opts } if opts.any? 12 | 13 | handle_response get "/Accounts/#{Twilio::Config.account_sid}/AvailablePhoneNumbers/#{country_code}/#{number_type}.json", params 14 | end 15 | 16 | private :new 17 | undef_method :count 18 | 19 | end 20 | 21 | # Shortcut for creating a new incoming phone number. Delegates to Twilio::IncomingPhoneNumber.create accepting the same options as that method does. 22 | def purchase!(opts={}) 23 | Twilio::IncomingPhoneNumber.create opts.update :phone_number => phone_number 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/twilio/call.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class Call 3 | include Twilio::Resource 4 | include Twilio::Persistable 5 | extend Twilio::Finder 6 | extend Twilio::Associations 7 | 8 | has_many :recordings, :notifications 9 | 10 | mutable_attributes :url, :method, :status 11 | 12 | class << self 13 | alias old_create create 14 | def create(attrs={}) 15 | attrs = attrs.with_indifferent_access 16 | attrs.each { |k,v| v.upcase! if k.to_s =~ /method$/ } 17 | attrs['if_machine'].try :capitalize 18 | old_create attrs 19 | end 20 | 21 | private 22 | def prepare_params(opts) # :nodoc: 23 | pairs = opts.map do |k,v| 24 | if %w(started_before started_after ended_before ended_after).include? k 25 | # Fancy schmancy-ness to handle Twilio <= URI operator for dates 26 | comparator = k =~ /before$/ ? '<=' : '>=' 27 | delineator = k =~ /started/ ? 'Start' : 'End' 28 | CGI.escape(delineator << "Time" << comparator << v.to_s) 29 | else 30 | "#{k.to_s.camelize}=#{CGI.escape v.to_s}" 31 | end 32 | end 33 | "?#{pairs.join('&')}" 34 | end 35 | end 36 | 37 | # Cancels a call if its state is 'queued' or 'ringing' 38 | def cancel! 39 | update_attributes :status => 'cancelled' 40 | end 41 | 42 | def complete! 43 | update_attributes :status => 'completed' 44 | end 45 | 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/twilio/capability.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | module CapabilityToken 3 | def create(opts={}) 4 | opts.stringify_keys! 5 | account_sid, auth_token = *credentials_for(opts) 6 | payload = { 7 | :exp => (opts.delete('expires') || 1.hour.from_now).to_i, 8 | :scope => opts.map { |k,v| send k, v, opts }.join(' '), 9 | :iss => account_sid 10 | } 11 | JWT.encode payload, auth_token 12 | end 13 | 14 | private 15 | 16 | def credentials_for(opts) 17 | if opts['account_sid'] && opts['auth_token'] 18 | [opts.delete('account_sid'), opts.delete('auth_token')] 19 | else 20 | [Twilio::Config.account_sid, Twilio::Config.auth_token] 21 | end 22 | end 23 | 24 | def allow_incoming(client_id, opts) 25 | token_for 'client', 'incoming', { 'clientName' => client_id } 26 | end 27 | 28 | def allow_outgoing(payload, opts) 29 | p = {} 30 | if payload.respond_to? :each 31 | p['appSid'] = payload.shift 32 | p['appParams'] = uri_encode payload.pop 33 | else # it's a string 34 | p['appSid'] = payload 35 | end 36 | p['clientName'] = opts['allow_incoming'] if opts['allow_incoming'] 37 | token_for 'client', 'outgoing', p 38 | end 39 | 40 | def uri_encode(hash) 41 | hash.map { |k,v| "#{CGI.escape k.to_s}=#{CGI.escape v}" }.join '&' 42 | end 43 | 44 | def token_for(service, privilege, params = {}) 45 | token = "scope:#{service}:#{privilege}" 46 | token << "?#{uri_encode params}" if params.any? 47 | end 48 | 49 | extend self 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/twilio/conference.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class Conference 3 | include Twilio::Resource 4 | extend Twilio::Finder 5 | 6 | def participants 7 | account_sid = self[:account_sid] if self[:connect_app_sid] 8 | res = self.class.get "/Accounts/#{self[:account_sid]}/Conferences/#{sid}/Participants.json", :account_sid => account_sid 9 | if (400..599).include? res.code 10 | raise Twilio::APIError.new "Error ##{res.parsed_response['code']}: #{res.parsed_response['message']}" 11 | else 12 | res.parsed_response['participants'].map do |p| 13 | p['api_version'] = p['api_version'].to_s # api_version parsed as a date by http_party 14 | Twilio::Participant.new p 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/twilio/config.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Twilio 4 | module Config 5 | attr_writer :account_sid, :auth_token 6 | 7 | def account_sid 8 | if @account_sid 9 | @account_sid 10 | else 11 | raise Twilio::ConfigurationError.new \ 12 | "Cannot complete request. Please set account_sid with Twilio::Config.setup first!" 13 | end 14 | end 15 | 16 | def auth_token 17 | if @auth_token 18 | @auth_token 19 | else 20 | raise Twilio::ConfigurationError.new \ 21 | "Cannot complete request. Please set auth_token with Twilio::Config.setup first!" 22 | end 23 | end 24 | 25 | def setup(opts={}, &blk) 26 | if block_given? 27 | instance_eval &blk 28 | warn 'The block syntax for configuration is deprecated. Use an options hash instead, e.g. Twilio::Config.setup account_sid: "AC00000000000000000000000", auth_token: "xxxxxxxxxxxxxxxxxxx"' 29 | else 30 | opts.map do |k,v| 31 | send("#{k}=", v) 32 | end 33 | end 34 | 35 | end 36 | 37 | def method_missing(meth, *args, &blk) 38 | const = meth.to_s.upcase 39 | Twilio.const_set(const, args.first) unless Twilio.const_defined? const 40 | end 41 | 42 | extend self 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/twilio/connect_app.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class ConnectApp 3 | include Twilio::Resource 4 | extend Twilio::Finder 5 | 6 | mutable_attributes :friendly_name, :authorize_redirect_url, :deauthorize_callback_url, :deauthorize_callback_method, :permissions, :description, :company_name, :homepage_url 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/twilio/deletable.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | module Deletable 3 | def destroy 4 | account_sid = self[:account_sid] if self[:connect_app_sid] 5 | state_guard { freeze && true if self.class.delete path, :account_sid => account_sid } 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/twilio/finder.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | module Finder 3 | def find(id, opts={}) 4 | opts = opts.with_indifferent_access 5 | # Support subaccounts by optionally passing in an account_sid or account object 6 | account_sid = opts.delete('account_sid') || opts.delete('account').try(:sid) || Twilio::Config.account_sid 7 | connect = opts.delete 'connect' 8 | 9 | res = get "#{resource_path(account_sid)}/#{id}.json", :account_sid => (connect && account_sid) 10 | hash = res.parsed_response 11 | if (200..299).include? res.code 12 | hash['api_version'] = hash['api_version'].to_s # api_version parsed as a date by http_party 13 | new hash 14 | end 15 | end 16 | 17 | def count(opts={}) 18 | opts = opts.with_indifferent_access 19 | # Support subaccounts by optionally passing in an account_sid or account object 20 | account_sid = opts.delete('account_sid') || opts.delete('account').try(:sid) || Twilio::Config.account_sid 21 | connect = opts.delete 'connect' 22 | 23 | params = prepare_params opts if opts.any? 24 | get("#{resource_path(account_sid)}.json#{params}", :account_sid => (connect && account_sid)).parsed_response['total'] 25 | end 26 | 27 | def all(opts={}) 28 | opts = opts.with_indifferent_access 29 | # Support subaccounts by optionally passing in an account_sid or account object 30 | account_sid = opts.delete('account_sid') || opts.delete('account').try(:sid) || Twilio::Config.account_sid 31 | connect = opts.delete 'connect' 32 | 33 | params = prepare_params opts if opts.any? 34 | handle_response get "#{resource_path(account_sid)}.json#{params}", :account_sid => (connect && account_sid) 35 | end 36 | 37 | private 38 | 39 | def resource_path(account_sid) 40 | "/Accounts/#{account_sid}/#{resource_name}" 41 | end 42 | 43 | def resource_name 44 | name.demodulize.pluralize 45 | end 46 | 47 | def prepare_params(opts) # :nodoc: 48 | pairs = opts.map do |k,v| 49 | if %w(updated_before created_before updated_after created_after).include? k 50 | # Fancy schmancy-ness to handle Twilio <= URI operator for dates 51 | comparator = k =~ /before$/ ? '<=' : '>=' 52 | CGI.escape("Date" << k.gsub(/\_\w+$/, '').capitalize << comparator << v.to_s) 53 | else 54 | "#{k.to_s.camelize}=#{CGI.escape v.to_s}" 55 | end 56 | end 57 | "?#{pairs.join('&')}" 58 | end 59 | 60 | def handle_response(res) # :nodoc: 61 | if (400..599).include? res.code 62 | raise Twilio::APIError.new "Error ##{res.parsed_response['code']}: #{res.parsed_response['message']}" 63 | else 64 | key = name.demodulize == 'SMS' ? 'sms_messages' : name.demodulize.underscore.pluralize 65 | res.parsed_response[key].map do |p| 66 | p['api_version'] = p['api_version'].to_s # api_version parsed as a date by http_party 67 | new p 68 | end 69 | end 70 | end 71 | 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/twilio/incoming_phone_number.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class IncomingPhoneNumber 3 | extend Twilio::Finder 4 | include Twilio::Resource 5 | include Twilio::Persistable 6 | include Twilio::Deletable 7 | 8 | mutable_attributes :friendly_name, :api_version, :voice_url, :voice_method, 9 | :voice_fallback_url, :voice_fallback_method, :status_callback, :status_callback_method, 10 | :sms_url, :sms_method, :sms_method, :sms_fallback_url, :sms_fallback_method, :voice_caller_id_lookup 11 | 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/twilio/notification.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class Notification 3 | include Twilio::Resource 4 | include Twilio::Deletable 5 | extend Twilio::Finder 6 | 7 | class << self 8 | private 9 | def prepare_params(opts) # :nodoc: 10 | pairs = opts.map do |k,v| 11 | if %w(created_before created_after).include? k 12 | # Fancy schmancy-ness to handle Twilio <= URI operator for dates 13 | comparator = k =~ /before$/ ? '<=' : '>=' 14 | CGI.escape("MessageDate" << comparator << v.to_s) 15 | else 16 | "#{k.to_s.camelize}=#{CGI.escape v.to_s}" 17 | end 18 | end 19 | "?#{pairs.join('&')}" 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/twilio/outgoing_caller_id.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class OutgoingCallerId 3 | include Twilio::Resource 4 | include Twilio::Deletable 5 | include Twilio::Persistable 6 | extend Twilio::Finder 7 | 8 | mutable_attributes :friendly_name 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/twilio/participant.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class Participant 3 | include Twilio::Resource 4 | include Twilio::Deletable 5 | 6 | mutable_attributes :muted 7 | 8 | alias kick! destroy 9 | 10 | def mute! 11 | state_guard do 12 | update_attributes muted? ? { :muted => false } : { :muted => true } 13 | end 14 | end 15 | 16 | def muted? 17 | muted 18 | end 19 | 20 | private 21 | 22 | def path 23 | "/Accounts/#{account_sid}/Conferences/#{conference_sid}/Participants/#{call_sid}.json" 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/twilio/persistable.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | module Persistable 3 | 4 | def self.included(base) 5 | base.instance_eval do 6 | def create(attrs={}) 7 | attrs = attrs.with_indifferent_access 8 | # Support subaccounts by optionally passing in an account_sid or account object 9 | account_sid = attrs.delete('account_sid') || attrs.delete('account').try(:sid) || Twilio::Config.account_sid 10 | connect = attrs.delete 'connect' 11 | request_opts = { :body => Hash[attrs.map { |k,v| [k.to_s.camelize, v]}] } 12 | 13 | request_opts.update(:account_sid => account_sid) if connect 14 | 15 | res = post "#{resource_path(account_sid)}.json", request_opts 16 | 17 | if (400..599).include? res.code 18 | raise Twilio::APIError.new "Error ##{res.parsed_response['code']}: #{res.parsed_response['message']}" 19 | else 20 | res.parsed_response['api_version'] = res.parsed_response['api_version'].to_s 21 | new res.parsed_response 22 | end 23 | end 24 | 25 | private 26 | def resource_path(account_sid) 27 | "/Accounts/#{account_sid}/#{resource_name}" 28 | end 29 | 30 | def resource_name 31 | name.demodulize.pluralize 32 | end 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/twilio/queue.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class Queue 3 | extend Twilio::Finder 4 | include Twilio::Resource 5 | include Twilio::Persistable 6 | include Twilio::Deletable 7 | 8 | mutable_attributes :sid, :friendly_name, :current_size, 9 | :max_size, :average_wait_time 10 | 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/twilio/recording.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class Recording 3 | include Twilio::Resource 4 | include Twilio::Deletable 5 | extend Twilio::Finder 6 | 7 | def mp3 8 | API_ENDPOINT + path.gsub(/\.json$/, '.mp3') 9 | end 10 | 11 | def wav 12 | API_ENDPOINT + path.gsub(/\.json$/, '.wav') 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/twilio/request_filter.rb: -------------------------------------------------------------------------------- 1 | require 'base64' 2 | require 'openssl' 3 | require 'digest/sha1' 4 | 5 | module Twilio 6 | module RequestFilter 7 | def filter(controller) 8 | request = controller.request 9 | if request.format.try(:voice?) 10 | controller.head(:forbidden) if expected_signature_for(request) != (request.env['HTTP_X_TWILIO_SIGNATURE'] || request.env['X-Twilio-Signature']) 11 | end 12 | end 13 | alias before filter 14 | 15 | private 16 | def expected_signature_for(request) 17 | string_to_sign = request.url + request.request_parameters.sort.join 18 | digest = OpenSSL::Digest::Digest.new('sha1') 19 | hash = OpenSSL::HMAC.digest(digest, Twilio::Config.auth_token, string_to_sign) 20 | 21 | Base64.encode64(hash).strip 22 | end 23 | 24 | extend self 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/twilio/resource.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/core_ext/string' # Chill! we only use the bits of AS we need! 2 | require 'active_support/core_ext/hash' 3 | require 'active_support/core_ext/array/extract_options' 4 | 5 | 6 | module Twilio 7 | module Resource 8 | def initialize(attrs={}) #:nodoc: 9 | @attributes = attrs.with_indifferent_access 10 | end 11 | 12 | def inspect 13 | "#<#{self.class} #{@attributes.map { |k,v| "#{k}: #{v.inspect}" }.join ', '}>" 14 | end 15 | 16 | def [](key) 17 | attributes[key] 18 | end 19 | 20 | def []=(key,value) # :nodoc: 21 | attributes[key] = value 22 | end 23 | 24 | def update_attributes(attrs={}) 25 | state_guard do 26 | # state account sid if this is a resource created with a connect subaccount 27 | account_sid = self[:account_sid] if self[:connect_app_sid] 28 | handle_response klass.post path, :body => Hash[attrs.map { |k,v| [k.to_s.camelize, v]}], :account_sid => account_sid 29 | end 30 | end 31 | 32 | private 33 | def resource_name #:nodoc: 34 | klass.name.demodulize.pluralize 35 | end 36 | 37 | def klass #:nodoc: 38 | self.class == Module ? self : self.class 39 | end 40 | 41 | def state_guard #:nodoc: 42 | if frozen? 43 | raise RuntimeError, "#{self.class.name.demodulize} has already been destroyed" 44 | else 45 | yield 46 | end 47 | end 48 | 49 | def path #:nodoc: 50 | uri[11,uri.length] 51 | end 52 | 53 | def handle_response(res) # :nodoc: 54 | if (400..599).include? res.code 55 | raise Twilio::APIError.new "Error ##{res.parsed_response['code']}: #{res.parsed_response['message']}" 56 | else 57 | res.parsed_response['api_version'] = res.parsed_response['api_version'].to_s 58 | @attributes.update(res.parsed_response) 59 | end 60 | end 61 | 62 | def method_missing(id, *args, &blk) #:nodoc: 63 | meth = id.to_s 64 | if meth =~ /\=$/ 65 | add_attr_writer meth 66 | send meth, args.first 67 | elsif meth =~ /^#{meth}\?$/i 68 | add_predicate meth 69 | send meth 70 | elsif attributes.keys.include? meth 71 | add_attr_reader meth 72 | send meth 73 | else 74 | super 75 | end 76 | end 77 | 78 | def add_predicate(attribute) # :nodoc: 79 | metaclass.class_eval do 80 | define_method(attribute) { self['status'] =~ /^#{attribute.gsub '?', ''}/i ? true : false } 81 | end 82 | end 83 | 84 | def add_attr_writer(attribute) # :nodoc: 85 | metaclass.class_eval do 86 | define_method(attribute) { |value| self[attribute.to_s.gsub(/\=$/, '').to_sym] = value } 87 | end 88 | end 89 | 90 | def add_attr_reader(attribute) #:nodoc: 91 | metaclass.class_eval do 92 | define_method(attribute) { self[attribute] } 93 | end 94 | end 95 | 96 | def metaclass #:nodoc: 97 | class << self; self; end 98 | end 99 | 100 | def self.included(base) 101 | base.instance_eval do 102 | include HTTParty 103 | attr_reader :attributes 104 | format :json 105 | base_uri Twilio::API_ENDPOINT 106 | end 107 | 108 | class << base 109 | # decorate http methods with authentication 110 | %w.each do |meth| 111 | define_method(meth) do |*args| # splatted args necessary hack since <= 1.8.7 does not support optional block args 112 | opts = args.extract_options! 113 | account_sid = opts.delete :account_sid 114 | 115 | # if account sid is passed in as an option use it for basic auth (twilio connect) 116 | super args.first, opts.merge(:headers => { 'User-Agent' => 'twilio-rb/2.1.1' }, :basic_auth => { :username => account_sid || Twilio::Config.account_sid, :password => Twilio::Config.auth_token }) 117 | end 118 | end 119 | 120 | def mutable_attributes(*attrs) 121 | attrs.each do |attr| 122 | define_method "#{attr}=" do |arg| 123 | update_attributes attr => arg 124 | end 125 | end 126 | end 127 | end 128 | end 129 | end 130 | end 131 | -------------------------------------------------------------------------------- /lib/twilio/sandbox.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | module Sandbox 3 | include Twilio::Resource 4 | @attributes = {}.with_indifferent_access 5 | 6 | def attributes 7 | @attributes.empty? ? reload! : @attributes 8 | end 9 | 10 | def reload! 11 | handle_response get path 12 | end 13 | 14 | %w.each do |meth| 15 | define_method "#{meth}=" do |arg| 16 | update_attributes meth => arg 17 | end 18 | end 19 | 20 | private 21 | def path 22 | "/Accounts/#{Twilio::Config.account_sid}/Sandbox.json" 23 | end 24 | extend self 25 | end 26 | end -------------------------------------------------------------------------------- /lib/twilio/short_code.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class ShortCode 3 | include Twilio::Resource 4 | extend Twilio::Finder 5 | 6 | class << self 7 | private 8 | 9 | def resource_name 10 | "SMS/ShortCodes" 11 | end 12 | end 13 | 14 | mutable_attributes :friendly_name, :api_version, :sms_url, :sms_method, 15 | :sms_fallback_url, :sms_fallback_method 16 | 17 | private 18 | def resource_name 19 | "SMS/ShortCodes" 20 | end 21 | 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/twilio/sms.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class SMS 3 | include Twilio::Resource 4 | include Twilio::Persistable 5 | extend Twilio::Finder 6 | 7 | class << self 8 | private 9 | def resource_name 10 | "SMS/Messages" 11 | end 12 | 13 | def prepare_params(opts) # :nodoc: 14 | pairs = opts.map do |k,v| 15 | if %w(created_before created_after sent_before sent_after).include? k 16 | # Fancy schmancy-ness to handle Twilio <= URI operator for dates 17 | comparator = k =~ /before$/ ? '<=' : '>=' 18 | URI.encode("DateSent" << comparator << v.to_s) 19 | else 20 | "#{k.to_s.camelize}=#{CGI.escape v.to_s}" 21 | end 22 | end 23 | "?#{pairs.join('&')}" 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/twilio/transcription.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | class Transcription 3 | include Twilio::Resource 4 | extend Twilio::Finder 5 | end 6 | end -------------------------------------------------------------------------------- /lib/twilio/twiml.rb: -------------------------------------------------------------------------------- 1 | module Twilio 2 | module TwiML 3 | def build &blk 4 | xm = Builder::XmlMarkup.new(:indent => 2) 5 | xm.instance_eval do 6 | def method_missing(meth, *args, &blk) 7 | # camelize options 8 | if args.last.kind_of? ::Hash 9 | args[-1] = ::Hash[args.last.map { |k,v| [k.to_s.camelize(:lower), v]}] 10 | end 11 | # let builder do the heavy lifting 12 | super(meth.to_s.capitalize, *args, &blk) 13 | end 14 | end 15 | xm.instruct! 16 | xm.response &blk 17 | end 18 | extend self 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /rakefile: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), 'lib', 'twilio-rb') 2 | require 'rspec' 3 | require 'rspec/core/rake_task' 4 | 5 | RSpec::Core::RakeTask.new do |t| 6 | t.rspec_opts = %w<-c> 7 | t.pattern = 'spec/*.rb' 8 | end 9 | 10 | task :default => :spec 11 | -------------------------------------------------------------------------------- /spec/account_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Twilio::Account do 4 | 5 | before { Twilio::Config.setup :account_sid => 'AC000000000000', :auth_token => '79ad98413d911947f0ba369d295ae7a3' } 6 | 7 | def resource_uri(account_sid=nil) 8 | account_sid ||= Twilio::Config.account_sid 9 | "https://#{Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts" 10 | end 11 | 12 | def stub_api_call(response_file, account_sid=nil) 13 | stub_request(:get, resource_uri(account_sid) + '.json'). 14 | to_return :body => canned_response(response_file), :status => 200 15 | end 16 | 17 | post_body = "FriendlyName=REST%20test" 18 | 19 | let(:params) { { :friendly_name => 'REST test' } } 20 | 21 | let(:account) { Twilio::Account.create params } 22 | 23 | describe '.count' do 24 | before { stub_api_call 'list_accounts' } 25 | it 'returns the account count' do 26 | Twilio::Account.count.should == 6 27 | end 28 | 29 | it 'accepts options to refine the search' do 30 | query = '.json?FriendlyName=example' 31 | stub_request(:get, resource_uri + query). 32 | to_return :body => canned_response('list_accounts'), :status => 200 33 | Twilio::Account.count :friendly_name => 'example' 34 | a_request(:get, resource_uri + query).should have_been_made 35 | end 36 | end 37 | 38 | describe '.all' do 39 | before { stub_api_call 'list_accounts' } 40 | let(:resp) { resp = Twilio::Account.all } 41 | it 'returns a collection of objects with a length corresponding to the response' do 42 | resp.length.should == 1 43 | end 44 | 45 | it 'returns a collection containing instances of Twilio::Account' do 46 | resp.all? { |r| r.is_a? Twilio::Account }.should be_true 47 | end 48 | 49 | JSON.parse(canned_response('list_accounts'))['accounts'].each_with_index do |obj,i| 50 | obj.each do |attr, value| 51 | specify { resp[i].send(attr).should == value } 52 | end 53 | end 54 | 55 | it 'accepts options to refine the search' do 56 | query = '.json?FriendlyName=example&Page=5' 57 | stub_request(:get, resource_uri + query). 58 | to_return :body => canned_response('list_accounts'), :status => 200 59 | Twilio::Account.all :page => 5, :friendly_name => 'example' 60 | a_request(:get, resource_uri + query).should have_been_made 61 | end 62 | end 63 | 64 | describe '.find' do 65 | context 'for a valid account' do 66 | before do 67 | stub_request(:get, resource_uri + '/AC2a0747eba6abf96b7e3c3ff0b4530f6e' + '.json'). 68 | to_return :body => canned_response('account'), :status => 200 69 | end 70 | 71 | let(:account) { Twilio::Account.find 'AC2a0747eba6abf96b7e3c3ff0b4530f6e' } 72 | 73 | it 'returns an instance of Twilio::Account' do 74 | account.should be_a Twilio::Account 75 | end 76 | 77 | JSON.parse(canned_response('account')).each do |k,v| 78 | specify { account.send(k).should == v } 79 | end 80 | end 81 | 82 | context 'for a string that does not correspond to a real account' do 83 | before do 84 | stub_request(:get, resource_uri + '/phony' + '.json').to_return :status => 404 85 | end 86 | it 'returns nil' do 87 | account = Twilio::Account.find 'phony' 88 | account.should be_nil 89 | end 90 | end 91 | end 92 | 93 | describe '.create' do 94 | before { stub_request(:post, resource_uri + '.json').with(:body => post_body).to_return :body => canned_response('account')} 95 | 96 | it 'creates a new incoming account on the account' do 97 | account 98 | a_request(:post, resource_uri + '.json').with(:body => post_body).should have_been_made 99 | end 100 | 101 | it 'returns an instance of Twilio::Account' do 102 | account.should be_a Twilio::Account 103 | end 104 | 105 | JSON.parse(canned_response('account')).map do |k,v| 106 | specify { account.send(k).should == v } 107 | end 108 | end 109 | 110 | 111 | describe '#update_attributes' do 112 | before do 113 | stub_request(:post, resource_uri + '.json').with(:body => post_body).to_return :body => canned_response('account') 114 | stub_request(:post, resource_uri + '/' + account.sid + '.json').with(:body => post_body). 115 | to_return :body => canned_response('account') 116 | end 117 | context 'when the resource has been persisted' do 118 | it 'updates the API account the new parameters' do 119 | account.update_attributes params 120 | a_request(:post, resource_uri + '/' + account.sid + '.json').with(:body => post_body).should have_been_made 121 | end 122 | end 123 | end 124 | 125 | %w.each do |meth| 126 | describe "##{meth}=" do 127 | let(:account) { Twilio::Account.create params } 128 | 129 | before do 130 | stub_request(:post, resource_uri + '.json').with(:body => post_body).to_return :body => canned_response('account') 131 | stub_request(:post, resource_uri + '/' + account.sid + '.json'). 132 | with(:body => URI.encode("#{meth.camelize}=foo")).to_return :body => canned_response('account'), :status => 201 133 | end 134 | 135 | it "updates the #{meth} property with the API" do 136 | account.send "#{meth}=", 'foo' 137 | a_request(:post, resource_uri + '/' + account.sid + '.json'). 138 | with(:body => URI.encode("#{meth.camelize}=foo")).should have_been_made 139 | end 140 | end 141 | end 142 | 143 | describe "#active?" do 144 | before { stub_request(:post, resource_uri + '.json').with(:body => post_body).to_return :body => canned_response('account') } 145 | it 'returns true when the account is active' do 146 | account.should be_active 147 | end 148 | it 'returns false when the account is inactive' do 149 | account.attributes['status'] = 'dead' 150 | account.should_not be_active 151 | end 152 | end 153 | describe "#suspended?" do 154 | before { stub_request(:post, resource_uri + '.json').with(:body => post_body).to_return :body => canned_response('account') } 155 | it 'returns true when the account is suspended' do 156 | account.attributes['status'] = 'suspended' 157 | account.should be_suspended 158 | end 159 | it 'returns false when the account not suspended' do 160 | account.should_not be_suspended 161 | end 162 | end 163 | %w.each do |meth| 164 | describe "##{meth}=" do 165 | before { stub_request(:post, resource_uri + '.json').with(:body => post_body).to_return :body => canned_response('account') } 166 | it 'updates the friendly name' do 167 | stub_request(:post, resource_uri + '/' + account.sid + '.json').with(:body => "#{meth.camelize}=foo").to_return :body => canned_response('account'), :status => 201 168 | account.send "#{meth}=", 'foo' 169 | a_request(:post, resource_uri + '/' + account.sid + '.json').with(:body => "#{meth.camelize}=foo").should have_been_made 170 | end 171 | end 172 | end 173 | 174 | describe 'associations' do 175 | describe 'has_many' do 176 | it 'delegates the method to the associated class with the account sid merged into the options' do 177 | stub_request(:post, resource_uri + '.json').with(:body => post_body).to_return :body => canned_response('account') 178 | [:calls, :recordings, :conferences, :incoming_phone_numbers, :notifications, :outgoing_caller_ids, :transcriptions].each do |association| 179 | klass = Twilio.const_get association.to_s.classify 180 | klass.expects(:foo).with :account_sid => account.sid 181 | account.send(association).foo 182 | end 183 | end 184 | 185 | context 'where the account is a connect subaccount' do 186 | it 'delegates the method to the associated class with the account sid merged into the options' do 187 | account = Twilio::Account.new JSON.parse(canned_response('connect_account')) 188 | [:calls, :recordings, :conferences, :incoming_phone_numbers, :notifications, :outgoing_caller_ids, :transcriptions].each do |association| 189 | klass = Twilio.const_get association.to_s.classify 190 | klass.expects(:foo).with :account_sid => account.sid, :connect => true 191 | account.send(association).foo 192 | end 193 | end 194 | end 195 | end 196 | end 197 | end 198 | -------------------------------------------------------------------------------- /spec/authorized_connect_app_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Twilio::AuthorizedConnectApp do 4 | 5 | before { Twilio::Config.setup :account_sid => 'AC228ba7a5fe4238be081ea6f3c44186f3', :auth_token => '79ad98413d911947f0ba369d295ae7a3' } 6 | 7 | 8 | def resource_uri(account_sid=nil, connect=nil) 9 | account_sid ||= Twilio::Config.account_sid 10 | "https://#{connect ? account_sid : Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts/#{account_sid}/AuthorizedConnectApps" 11 | end 12 | 13 | def stub_api_call(response_file, account_sid=nil) 14 | stub_request(:get, resource_uri(account_sid) + '.json'). 15 | to_return :body => canned_response(response_file), :status => 200 16 | end 17 | 18 | describe '.all' do 19 | context 'using a twilio connect subaccount' do 20 | it 'uses the account sid as the username for basic auth' do 21 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 22 | to_return :body => canned_response('list_authorized_connect_apps'), :status => 200 23 | Twilio::AuthorizedConnectApp.all :account_sid => 'AC0000000000000000000000000000', :connect => true 24 | end 25 | end 26 | 27 | before { stub_api_call 'list_authorized_connect_apps' } 28 | it 'returns a collection of objects with a length corresponding to the response' do 29 | resp = Twilio::AuthorizedConnectApp.all 30 | resp.length.should == 1 31 | end 32 | 33 | it 'returns a collection containing instances of Twilio::AuthorizedConnectApp' do 34 | resp = Twilio::AuthorizedConnectApp.all 35 | resp.all? { |r| r.is_a? Twilio::AuthorizedConnectApp }.should be_true 36 | end 37 | 38 | JSON.parse(canned_response('list_authorized_connect_apps'))['authorized_connect_apps'].each_with_index do |obj,i| 39 | obj.each do |attr, value| 40 | specify { Twilio::AuthorizedConnectApp.all[i].send(attr).should == value } 41 | end 42 | end 43 | 44 | it 'accepts options to refine the search' do 45 | query = '.json?FriendlyName=barry&Page=5' 46 | stub_request(:get, resource_uri + query). 47 | to_return :body => canned_response('list_authorized_connect_apps'), :status => 200 48 | Twilio::AuthorizedConnectApp.all :page => 5, :friendly_name => 'barry' 49 | a_request(:get, resource_uri + query).should have_been_made 50 | end 51 | 52 | context 'on a subaccount' do 53 | before { stub_api_call 'list_authorized_connect_apps', 'SUBACCOUNT_SID' } 54 | 55 | context 'found by passing in an account_sid' do 56 | it 'uses the subaccount sid in the request' do 57 | Twilio::AuthorizedConnectApp.all :account_sid => 'SUBACCOUNT_SID' 58 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 59 | end 60 | end 61 | 62 | context 'found by passing in an instance of Twilio::Account' do 63 | it 'uses the subaccount sid in the request' do 64 | Twilio::AuthorizedConnectApp.all :account => mock(:sid => 'SUBACCOUNT_SID') 65 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 66 | end 67 | end 68 | end 69 | end 70 | 71 | describe '.count' do 72 | context 'using a twilio connect subaccount' do 73 | it 'uses the account sid as the username for basic auth' do 74 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 75 | to_return :body => canned_response('list_authorized_connect_apps'), :status => 200 76 | Twilio::AuthorizedConnectApp.count :account_sid => 'AC0000000000000000000000000000', :connect => true 77 | end 78 | end 79 | 80 | before { stub_api_call 'list_authorized_connect_apps' } 81 | it 'returns the number of resources' do 82 | Twilio::AuthorizedConnectApp.count.should == 3 83 | end 84 | 85 | it 'accepts options to refine the search' do 86 | query = '.json?FriendlyName=example' 87 | stub_request(:get, resource_uri + query). 88 | to_return :body => canned_response('list_authorized_connect_apps'), :status => 200 89 | Twilio::AuthorizedConnectApp.count :friendly_name => 'example' 90 | a_request(:get, resource_uri + query).should have_been_made 91 | end 92 | 93 | context 'on a subaccount' do 94 | before { stub_api_call 'list_authorized_connect_apps', 'SUBACCOUNT_SID' } 95 | 96 | context 'found by passing in an account_sid' do 97 | it 'uses the subaccount sid in the request' do 98 | Twilio::AuthorizedConnectApp.count :account_sid => 'SUBACCOUNT_SID' 99 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 100 | end 101 | end 102 | 103 | context 'found by passing in an instance of Twilio::Account' do 104 | it 'uses the subaccount sid in the request' do 105 | Twilio::AuthorizedConnectApp.count :account => mock(:sid => 'SUBACCOUNT_SID') 106 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 107 | end 108 | end 109 | end 110 | end 111 | 112 | describe '.find' do 113 | context 'using a twilio connect subaccount' do 114 | it 'uses the account sid as the username for basic auth' do 115 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '/CN47260e643654388faabe8aaa18ea6756.json' ). 116 | to_return :body => canned_response('list_authorized_connect_apps'), :status => 200 117 | Twilio::AuthorizedConnectApp.find 'CN47260e643654388faabe8aaa18ea6756', :account_sid => 'AC0000000000000000000000000000', :connect => true 118 | end 119 | end 120 | 121 | context 'for a valid authorized_connect_app' do 122 | before do 123 | stub_request(:get, resource_uri + '/CN47260e643654388faabe8aaa18ea6756' + '.json'). 124 | to_return :body => canned_response('authorized_connect_app'), :status => 200 125 | end 126 | let(:authorized_connect_app) { Twilio::AuthorizedConnectApp.find 'CN47260e643654388faabe8aaa18ea6756' } 127 | 128 | it 'returns an instance of Twilio::AuthorizedConnectApp.all' do 129 | authorized_connect_app.should be_a Twilio::AuthorizedConnectApp 130 | end 131 | 132 | JSON.parse(canned_response('authorized_connect_app')).each do |k,v| 133 | specify { authorized_connect_app.send(k).should == v } 134 | end 135 | end 136 | 137 | context 'for a string that does not correspond to a real authorized_connect_app' do 138 | before do 139 | stub_request(:get, resource_uri + '/phony' + '.json').to_return :status => 404 140 | end 141 | it 'returns nil' do 142 | authorized_connect_app = Twilio::AuthorizedConnectApp.find 'phony' 143 | authorized_connect_app.should be_nil 144 | end 145 | end 146 | 147 | context 'on a subaccount' do 148 | before do 149 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + '/CN47260e643654388faabe8aaa18ea6756' + '.json'). 150 | to_return :body => canned_response('authorized_connect_app'), :status => 200 151 | end 152 | 153 | context 'found by passing in an account_sid' do 154 | it 'uses the subaccount sid in the request' do 155 | Twilio::AuthorizedConnectApp.find 'CN47260e643654388faabe8aaa18ea6756', :account_sid => 'SUBACCOUNT_SID' 156 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/CN47260e643654388faabe8aaa18ea6756' + '.json'). 157 | should have_been_made 158 | end 159 | end 160 | 161 | context 'found by passing in an instance of Twilio::Account' do 162 | it 'uses the subaccount sid in the request' do 163 | Twilio::AuthorizedConnectApp.find 'CN47260e643654388faabe8aaa18ea6756', :account => mock(:sid => 'SUBACCOUNT_SID') 164 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/CN47260e643654388faabe8aaa18ea6756' + '.json'). 165 | should have_been_made 166 | end 167 | end 168 | end 169 | end 170 | end 171 | 172 | -------------------------------------------------------------------------------- /spec/available_phone_number_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Twilio::AvailablePhoneNumber do 4 | 5 | let(:resource_uri) { "https://#{Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts/#{Twilio::Config.account_sid}/AvailablePhoneNumbers" } 6 | before { Twilio::Config.setup :account_sid => 'AC000000000000', :auth_token => '79ad98413d911947f0ba369d295ae7a3' } 7 | 8 | def stub_api_call(uri_tail, response_file) 9 | stub_request(:get, resource_uri + uri_tail). 10 | to_return :body => canned_response(response_file), :status => 200 11 | end 12 | 13 | describe '.new' do 14 | it 'is a private method because they cannot be created via theTwilio API' do 15 | # I think this will fail on 1.8.7 as querying an objects methods returns a collection of strings. 16 | Twilio::AvailablePhoneNumber.private_methods.should include :new 17 | end 18 | end 19 | 20 | describe '.all' do 21 | context 'for US local numbers' do 22 | before { stub_api_call '/US/Local.json?AreaCode=510&Page=2', 'available_local_phone_numbers' } 23 | let(:resp) { Twilio::AvailablePhoneNumber.all :page => 2, :area_code => '510' } 24 | 25 | it 'returns a collection of objects with a length corresponding to the response' do 26 | resp = Twilio::AvailablePhoneNumber.all :page => 2, :area_code => '510' 27 | resp.length.should == 2 28 | end 29 | 30 | it 'returns a collection containing instances of Twilio::AvailablePhoneNumber' do 31 | resp = Twilio::AvailablePhoneNumber.all :page => 2, :area_code => '510' 32 | resp.all? { |r| r.is_a? Twilio::AvailablePhoneNumber }.should be_true 33 | end 34 | 35 | JSON.parse(canned_response('available_local_phone_numbers'))['available_phone_numbers'].each_with_index do |obj,i| 36 | obj.each { |attr, value| specify { resp[i].send(attr).should == value } } 37 | end 38 | end 39 | 40 | context 'for non-US local numbers' do 41 | before { stub_api_call '/CA/Local.json', 'available_local_phone_numbers' } 42 | it 'makes a request for a non-US number as per the country code' do 43 | Twilio::AvailablePhoneNumber.all :iso_country_code => 'CA' 44 | a_request(:get, resource_uri + '/CA/Local.json').should have_been_made 45 | end 46 | end 47 | 48 | context 'for US toll free numbers' do 49 | before { stub_api_call '/US/TollFree.json?Contains=STORM', 'available_toll_free_phone_numbers' } 50 | 51 | it 'returns a collection of objects with a length corresponding to the response' do 52 | resp = Twilio::AvailablePhoneNumber.all :toll_free => true, :contains => 'STORM' 53 | resp.length.should == 1 54 | end 55 | 56 | its 'collection contains instances of Twilio::AvailablePhoneNumber' do 57 | resp = Twilio::AvailablePhoneNumber.all :toll_free => true, :contains => 'STORM' 58 | resp.all? { |r| r.is_a? Twilio::AvailablePhoneNumber }.should be_true 59 | end 60 | 61 | its 'collection contains objects whose attributes correspond to the response' do 62 | numbers = JSON.parse(canned_response('available_toll_free_phone_numbers'))['available_phone_numbers'] 63 | resp = Twilio::AvailablePhoneNumber.all :toll_free => true, :contains => 'STORM' 64 | 65 | numbers.each_with_index do |obj,i| 66 | obj.each { |attr, value| resp[i].send(attr).should == value } 67 | end 68 | end 69 | end 70 | end 71 | 72 | describe '#purchase!' do 73 | it "delegates to Twilio::IncomingPhoneNumber.create merging self.phone_number with any given args" do 74 | Twilio::IncomingPhoneNumber.expects(:create).with :phone_number => '+12125550000', 75 | :voice_url => 'http://www.example.com/twiml.xml', :voice_method => 'post' 76 | available_number = Twilio::AvailablePhoneNumber.send :new, :phone_number => '+12125550000' 77 | available_number.purchase! :voice_url => 'http://www.example.com/twiml.xml', :voice_method => 'post' 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /spec/capability_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper') 2 | 3 | def parse_scope(scope) 4 | scope.scan(/scope:(client|stream):(incoming|outgoing)\?(\S+)/).map do |service, privilege, query| 5 | [service, privilege, CGI.parse(query)] 6 | end.flatten 7 | end 8 | 9 | describe 'Twilio::CapabilityToken' do 10 | before { Twilio::Config.setup :account_sid => 'AC000000000000', :auth_token => '79ad98413d911947f0ba369d295ae7a3' } 11 | 12 | describe '.create' do 13 | it 'sets iss in payload' do 14 | token = Twilio::CapabilityToken.create \ 15 | :allow_incoming => 'client_identifier', 16 | :allow_outgoing => 'APXXXXXXXXXXXXXXXXXXXXX' 17 | 18 | decoded = JWT.decode token, Twilio::Config.auth_token 19 | decoded['iss'].should == Twilio::Config.account_sid 20 | end 21 | 22 | context 'when no specified expiry time is given' do 23 | Timecop.freeze do 24 | it 'sets ttl as 1 hour from current time' do 25 | token = Twilio::CapabilityToken.create \ 26 | :allow_incoming => 'client_identifier', 27 | :allow_outgoing => 'APXXXXXXXXXXXXXXXXXXXXX' 28 | 29 | decoded = JWT.decode token, Twilio::Config.auth_token 30 | decoded['exp'].should == 1.hour.from_now.to_i 31 | end 32 | end 33 | end 34 | 35 | context 'when an expiry time is explicitly passed' do 36 | it 'sets ttl as given time' do 37 | Timecop.freeze do 38 | token = Twilio::CapabilityToken.create \ 39 | :allow_incoming => 'client_identifier', 40 | :allow_outgoing => 'APXXXXXXXXXXXXXXXXXXXXX', 41 | :expires => 4.hours.from_now 42 | 43 | decoded = JWT.decode token, Twilio::Config.auth_token 44 | decoded['exp'].should == 4.hours.from_now.to_i 45 | end 46 | end 47 | end 48 | 49 | it 'sets up the correct scopes' do 50 | token = Twilio::CapabilityToken.create \ 51 | :allow_incoming => 'client_identifier', 52 | :allow_outgoing => ['APXXXXXXXXXXXXXXXXXXXXX', { :app_param => 'foo' }] 53 | scopes = JWT.decode(token, Twilio::Config.auth_token)['scope'].split 54 | scopes.one? do |scope| 55 | parse_scope(scope) == ["client", "incoming", {"clientName"=>["client_identifier"]}] 56 | end.should be_true 57 | 58 | # client outgoing scope should know about client name if there is an incoming capability 59 | scopes.one? do |scope| 60 | parse_scope(scope) == ["client", "outgoing", {"appSid"=>["APXXXXXXXXXXXXXXXXXXXXX"], "appParams"=>["app_param=foo"], "clientName"=>["client_identifier"]}] 61 | end.should be_true 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /spec/conference_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Twilio::Conference do 4 | 5 | before { Twilio::Config.setup :account_sid => 'AC5ef872f6da5a21de157d80997a64bd33', :auth_token => '79ad98413d911947f0ba369d295ae7a3' } 6 | 7 | def resource_uri(account_sid=nil) 8 | account_sid ||= Twilio::Config.account_sid 9 | "https://#{Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts/#{account_sid}/Conferences" 10 | end 11 | 12 | def resource_uri(account_sid=nil, connect=nil) 13 | account_sid ||= Twilio::Config.account_sid 14 | "https://#{connect ? account_sid : Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts/#{account_sid}/Conferences" 15 | end 16 | 17 | def stub_api_call(response_file, account_sid=nil) 18 | stub_request(:get, resource_uri(account_sid) + '.json'). 19 | to_return :body => canned_response(response_file), :status => 200 20 | end 21 | 22 | describe '.all' do 23 | 24 | context 'using a twilio connect subaccount' do 25 | it 'uses the account sid as the username for basic auth' do 26 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 27 | to_return :body => canned_response('list_connect_conferences'), :status => 200 28 | Twilio::Conference.all :account_sid => 'AC0000000000000000000000000000', :connect => true 29 | end 30 | end 31 | 32 | context 'context on the master account' do 33 | before { stub_api_call 'list_conferences' } 34 | it 'returns a collection of objects with a length corresponding to the response' do 35 | resp = Twilio::Conference.all 36 | resp.length.should == 1 37 | end 38 | 39 | it 'returns a collection containing instances of Twilio::Conference' do 40 | resp = Twilio::Conference.all 41 | resp.all? { |r| r.is_a? Twilio::Conference }.should be_true 42 | end 43 | 44 | it 'returns a collection containing objects with attributes corresponding to the response' do 45 | conferences = JSON.parse(canned_response('list_conferences'))['conferences'] 46 | resp = Twilio::Conference.all 47 | 48 | conferences.each_with_index do |obj,i| 49 | obj.each do |attr, value| 50 | resp[i].send(attr).should == value 51 | end 52 | end 53 | end 54 | 55 | it 'accepts options to refine the search' do 56 | query = '.json?FriendlyName=example&Status=in-progress&Page=5&DateCreated>=1970-01-01&DateUpdated<=2038-01-19' 57 | stub_request(:get, resource_uri + query).to_return :body => canned_response('list_conferences'), :status => 200 58 | Twilio::Conference.all :page => 5, :friendly_name => 'example', :status => 'in-progress', 59 | :created_after => Date.parse('1970-01-01'), :updated_before => Date.parse('2038-01-19') 60 | a_request(:get, resource_uri + query).should have_been_made 61 | end 62 | end 63 | 64 | context 'on a subaccount' do 65 | before { stub_api_call 'list_conferences', 'SUBACCOUNT_SID' } 66 | 67 | context 'found by passing in an account_sid' do 68 | it 'uses the subaccount sid in the request' do 69 | Twilio::Conference.all :account_sid => 'SUBACCOUNT_SID' 70 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 71 | end 72 | end 73 | 74 | context 'found by passing in an instance of Twilio::Account' do 75 | it 'uses the subaccount sid in the request' do 76 | Twilio::Conference.all :account => mock(:sid => 'SUBACCOUNT_SID') 77 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 78 | end 79 | end 80 | end 81 | end 82 | 83 | describe '.count' do 84 | before { stub_api_call 'list_conferences' } 85 | 86 | context 'using a twilio connect subaccount' do 87 | it 'uses the account sid as the username for basic auth' do 88 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 89 | to_return :body => canned_response('list_connect_conferences'), :status => 200 90 | Twilio::Conference.count :account_sid => 'AC0000000000000000000000000000', :connect => true 91 | end 92 | end 93 | 94 | it 'returns the number of resources' do 95 | Twilio::Conference.count.should == 462 96 | end 97 | 98 | it 'accepts options to refine the search' do 99 | query = '.json?FriendlyName=example&Status=in-progress' 100 | stub_request(:get, resource_uri + query). 101 | to_return :body => canned_response('list_conferences'), :status => 200 102 | Twilio::Conference.count :friendly_name => 'example', :status => 'in-progress' 103 | a_request(:get, resource_uri + query).should have_been_made 104 | end 105 | 106 | context 'on a subaccount' do 107 | before { stub_api_call 'list_conferences', 'SUBACCOUNT_SID' } 108 | context 'found by passing in an account_sid' do 109 | it 'uses the subaccount sid in the request' do 110 | Twilio::Conference.count :account_sid => 'SUBACCOUNT_SID' 111 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 112 | end 113 | end 114 | 115 | context 'found by passing in an instance of Twilio::Account' do 116 | it 'uses the subaccount sid in the request' do 117 | Twilio::Conference.count :account => mock(:sid => 'SUBACCOUNT_SID') 118 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 119 | end 120 | end 121 | end 122 | end 123 | 124 | describe '.find' do 125 | context 'on a subaccount' do 126 | before do 127 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + '/CFbbe46ff1274e283f7e3ac1df0072ab39' + '.json'). 128 | to_return :body => canned_response('conference'), :status => 200 129 | end 130 | 131 | context 'using a twilio connect subaccount' do 132 | it 'uses the account sid as the username for basic auth' do 133 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '/CFbbe46ff1274e283f7e3ac1df0072ab39.json' ). 134 | to_return :body => canned_response('connect_conference'), :status => 200 135 | Twilio::Conference.find 'CFbbe46ff1274e283f7e3ac1df0072ab39', :account_sid => 'AC0000000000000000000000000000', :connect => true 136 | end 137 | end 138 | 139 | context 'found by passing in an account_sid' do 140 | it 'uses the subaccount sid in the request' do 141 | Twilio::Conference.find 'CFbbe46ff1274e283f7e3ac1df0072ab39', :account_sid => 'SUBACCOUNT_SID' 142 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/CFbbe46ff1274e283f7e3ac1df0072ab39' + '.json'). 143 | should have_been_made 144 | end 145 | end 146 | 147 | context 'found by passing in an instance of Twilio::Account' do 148 | it 'uses the subaccount sid in the request' do 149 | Twilio::Conference.find 'CFbbe46ff1274e283f7e3ac1df0072ab39', :account => mock(:sid => 'SUBACCOUNT_SID') 150 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/CFbbe46ff1274e283f7e3ac1df0072ab39' + '.json'). 151 | should have_been_made 152 | end 153 | end 154 | end 155 | 156 | context 'for a valid conference' do 157 | before do 158 | stub_request(:get, resource_uri + '/CFbbe46ff1274e283f7e3ac1df0072ab39' + '.json'). 159 | to_return :body => canned_response('conference'), :status => 200 160 | end 161 | 162 | it 'returns an instance of Twilio::Conference' do 163 | conference = Twilio::Conference.find 'CFbbe46ff1274e283f7e3ac1df0072ab39' 164 | conference.should be_a Twilio::Conference 165 | end 166 | 167 | it 'returns an object with attributes that correspond to the API response' do 168 | response = JSON.parse(canned_response('conference')) 169 | conference = Twilio::Conference.find 'CFbbe46ff1274e283f7e3ac1df0072ab39' 170 | response.each { |k,v| conference.send(k).should == v } 171 | end 172 | end 173 | 174 | context 'for a string that does not correspond to a real conference' do 175 | before do 176 | stub_request(:get, resource_uri + '/phony' + '.json').to_return :status => 404 177 | end 178 | it 'returns nil' do 179 | conference = Twilio::Conference.find 'phony' 180 | conference.should be_nil 181 | end 182 | end 183 | end 184 | 185 | describe '#participants' do 186 | before do 187 | stub_api_call 'list_conferences' 188 | stub_request(:get, resource_uri + '/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants.json'). 189 | to_return :body => canned_response('list_participants'), :status => 200 190 | end 191 | 192 | context 'using a twilio connect subaccount' do 193 | it 'uses the account sid as the username for basic auth' do 194 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '/CFbbe46ff1274e283f7e3ac1df0072ab39.json' ). 195 | to_return :body => canned_response('connect_conference'), :status => 200 196 | conference = Twilio::Conference.find 'CFbbe46ff1274e283f7e3ac1df0072ab39', :account_sid => 'AC0000000000000000000000000000', :connect => true 197 | 198 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants.json' ). 199 | to_return :body => canned_response('list_participants'), :status => 200 200 | conference.participants 201 | 202 | a_request(:get, resource_uri('AC0000000000000000000000000000', true) + '/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants.json' ). 203 | should have_been_made 204 | end 205 | end 206 | 207 | let(:resp) { Twilio::Conference.all.first.participants } 208 | 209 | it 'returns a collection of participants' do 210 | resp.should_not be_empty 211 | resp.all? { |r| r.is_a? Twilio::Participant }.should be_true 212 | end 213 | 214 | it 'returns a collection containing objects with attributes corresponding to the response' do 215 | participants = JSON.parse(canned_response('list_participants'))['participants'] 216 | 217 | participants.each_with_index do |obj,i| 218 | obj.each do |attr, value| 219 | resp[i].send(attr).should == value 220 | end 221 | end 222 | end 223 | 224 | it 'returns a collection with a length corresponding to the API response' do 225 | resp.length.should == 1 226 | end 227 | end 228 | 229 | end 230 | -------------------------------------------------------------------------------- /spec/config_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper') 2 | 3 | describe Twilio::Config do 4 | describe '.setup' do 5 | it 'assigns the options hash values to constants in the Twilio namespace that correspond to the key values' do 6 | Twilio::Config.setup :account_sid => 'AC000000000000', :auth_token => '79ad98413d911947f0ba369d295ae7a3' 7 | Twilio::Config.account_sid.should == 'AC000000000000' 8 | Twilio::Config.auth_token.should == '79ad98413d911947f0ba369d295ae7a3' 9 | end 10 | 11 | it 'allows changing the config after initial setup' do 12 | Twilio::Config.setup :account_sid => 'AC000000000000', :auth_token => '79ad98413d911947f0ba369d295ae7a3' 13 | Twilio::Config.setup :account_sid => 'BC000000000000', :auth_token => '19ad98413d911947f0ba369d295ae7a3' 14 | Twilio::Config.account_sid.should == 'BC000000000000' 15 | Twilio::Config.auth_token.should == '19ad98413d911947f0ba369d295ae7a3' 16 | end 17 | end 18 | 19 | describe '.auth_token' do 20 | it 'raises an exception when invoked before set' do 21 | expect { 22 | Twilio::Config.auth_token 23 | }.to raise_error(Twilio::ConfigurationError, \ 24 | "Cannot complete request. Please set auth_token with Twilio::Config.setup first!") 25 | end 26 | end 27 | 28 | describe '.account_sid' do 29 | it 'raises an exception when invoked before set' do 30 | expect { 31 | Twilio::Config.account_sid 32 | }.to raise_error(Twilio::ConfigurationError, \ 33 | "Cannot complete request. Please set account_sid with Twilio::Config.setup first!") 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/connect_app_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Twilio::ConnectApp do 4 | 5 | before { Twilio::Config.setup :account_sid => 'AC228ba7a5fe4238be081ea6f3c44186f3', :auth_token => '79ad98413d911947f0ba369d295ae7a3' } 6 | 7 | 8 | def resource_uri(account_sid=nil, connect=nil) 9 | account_sid ||= Twilio::Config.account_sid 10 | "https://#{connect ? account_sid : Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts/#{account_sid}/ConnectApps" 11 | end 12 | 13 | def stub_api_call(response_file, account_sid=nil) 14 | stub_request(:get, resource_uri(account_sid) + '.json'). 15 | to_return :body => canned_response(response_file), :status => 200 16 | end 17 | 18 | describe '.all' do 19 | before { stub_api_call 'list_connect_apps' } 20 | it 'returns a collection of objects with a length corresponding to the response' do 21 | resp = Twilio::ConnectApp.all 22 | resp.length.should == 1 23 | end 24 | 25 | it 'returns a collection containing instances of Twilio::ConnectApp' do 26 | resp = Twilio::ConnectApp.all 27 | resp.all? { |r| r.is_a? Twilio::ConnectApp }.should be_true 28 | end 29 | 30 | JSON.parse(canned_response('list_connect_apps'))['connect_apps'].each_with_index do |obj,i| 31 | obj.each do |attr, value| 32 | specify { Twilio::ConnectApp.all[i].send(attr).should == value } 33 | end 34 | end 35 | 36 | it 'accepts options to refine the search' do 37 | query = '.json?FriendlyName=barry&Page=5' 38 | stub_request(:get, resource_uri + query). 39 | to_return :body => canned_response('list_connect_apps'), :status => 200 40 | Twilio::ConnectApp.all :page => 5, :friendly_name => 'barry' 41 | a_request(:get, resource_uri + query).should have_been_made 42 | end 43 | 44 | context 'on a subaccount' do 45 | before { stub_api_call 'list_connect_apps', 'SUBACCOUNT_SID' } 46 | 47 | context 'found by passing in an account_sid' do 48 | it 'uses the subaccount sid in the request' do 49 | Twilio::ConnectApp.all :account_sid => 'SUBACCOUNT_SID' 50 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 51 | end 52 | end 53 | 54 | context 'found by passing in an instance of Twilio::Account' do 55 | it 'uses the subaccount sid in the request' do 56 | Twilio::ConnectApp.all :account => mock(:sid => 'SUBACCOUNT_SID') 57 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 58 | end 59 | end 60 | end 61 | end 62 | 63 | describe '.count' do 64 | before { stub_api_call 'list_connect_apps' } 65 | it 'returns the number of resources' do 66 | Twilio::ConnectApp.count.should == 3 67 | end 68 | 69 | it 'accepts options to refine the search' do 70 | query = '.json?FriendlyName=example' 71 | stub_request(:get, resource_uri + query). 72 | to_return :body => canned_response('list_connect_apps'), :status => 200 73 | Twilio::ConnectApp.count :friendly_name => 'example' 74 | a_request(:get, resource_uri + query).should have_been_made 75 | end 76 | 77 | context 'on a subaccount' do 78 | before { stub_api_call 'list_connect_apps', 'SUBACCOUNT_SID' } 79 | 80 | context 'found by passing in an account_sid' do 81 | it 'uses the subaccount sid in the request' do 82 | Twilio::ConnectApp.count :account_sid => 'SUBACCOUNT_SID' 83 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 84 | end 85 | end 86 | 87 | context 'found by passing in an instance of Twilio::Account' do 88 | it 'uses the subaccount sid in the request' do 89 | Twilio::ConnectApp.count :account => mock(:sid => 'SUBACCOUNT_SID') 90 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 91 | end 92 | end 93 | end 94 | end 95 | 96 | describe '.find' do 97 | context 'for a valid connect_app' do 98 | before do 99 | stub_request(:get, resource_uri + '/CNb989fdd207b04d16aee578018ef5fd93' + '.json'). 100 | to_return :body => canned_response('connect_app'), :status => 200 101 | end 102 | let(:connect_app) { Twilio::ConnectApp.find 'CNb989fdd207b04d16aee578018ef5fd93' } 103 | 104 | it 'returns an instance of Twilio::ConnectApp.all' do 105 | connect_app.should be_a Twilio::ConnectApp 106 | end 107 | 108 | JSON.parse(canned_response('connect_app')).each do |k,v| 109 | specify { connect_app.send(k).should == v } 110 | end 111 | end 112 | 113 | context 'for a string that does not correspond to a real connect_app' do 114 | before do 115 | stub_request(:get, resource_uri + '/phony' + '.json').to_return :status => 404 116 | end 117 | it 'returns nil' do 118 | connect_app = Twilio::ConnectApp.find 'phony' 119 | connect_app.should be_nil 120 | end 121 | end 122 | 123 | context 'on a subaccount' do 124 | before do 125 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + '/CNb989fdd207b04d16aee578018ef5fd93' + '.json'). 126 | to_return :body => canned_response('connect_app'), :status => 200 127 | end 128 | 129 | context 'found by passing in an account_sid' do 130 | it 'uses the subaccount sid in the request' do 131 | Twilio::ConnectApp.find 'CNb989fdd207b04d16aee578018ef5fd93', :account_sid => 'SUBACCOUNT_SID' 132 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/CNb989fdd207b04d16aee578018ef5fd93' + '.json'). 133 | should have_been_made 134 | end 135 | end 136 | 137 | context 'found by passing in an instance of Twilio::Account' do 138 | it 'uses the subaccount sid in the request' do 139 | Twilio::ConnectApp.find 'CNb989fdd207b04d16aee578018ef5fd93', :account => mock(:sid => 'SUBACCOUNT_SID') 140 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/CNb989fdd207b04d16aee578018ef5fd93' + '.json'). 141 | should have_been_made 142 | end 143 | end 144 | end 145 | end 146 | describe '#update_attributes=' do 147 | it 'updates the API number the new parameters' do 148 | connect_app = Twilio::ConnectApp.new JSON.parse(canned_response('connect_app')) 149 | stub_request(:post, resource_uri + '/' + connect_app.sid + '.json').with(:body => 'FriendlyName=foo'). 150 | to_return body: canned_response('connect_app'), status: 200 151 | connect_app.update_attributes :friendly_name => 'foo' 152 | a_request(:post, resource_uri + '/' + connect_app.sid + '.json').with(:body => 'FriendlyName=foo').should have_been_made 153 | end 154 | end 155 | end 156 | 157 | 158 | -------------------------------------------------------------------------------- /spec/notification_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Twilio::Notification do 4 | 5 | before { Twilio::Config.setup :account_sid => 'ACda6f1e11047ebd6fe7a55f120be3a900', :auth_token => '79ad98413d911947f0ba369d295ae7a3' } 6 | 7 | def resource_uri(account_sid=nil, connect=nil) 8 | account_sid ||= Twilio::Config.account_sid 9 | "https://#{connect ? account_sid : Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts/#{account_sid}/Notifications" 10 | end 11 | 12 | def stub_api_call(response_file, account_sid=nil) 13 | stub_request(:get, resource_uri(account_sid) + '.json'). 14 | to_return :body => canned_response(response_file), :status => 200 15 | end 16 | 17 | describe '.all' do 18 | context 'using a twilio connect subaccount' do 19 | it 'uses the account sid as the username for basic auth' do 20 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 21 | to_return :body => canned_response('list_connect_notifications'), :status => 200 22 | Twilio::Notification.all :account_sid => 'AC0000000000000000000000000000', :connect => true 23 | end 24 | end 25 | 26 | before { stub_api_call 'list_notifications' } 27 | it 'returns a collection of objects with a length corresponding to the response' do 28 | resp = Twilio::Notification.all 29 | resp.length.should == 1 30 | end 31 | 32 | it 'returns a collection containing instances of Twilio::Notification' do 33 | resp = Twilio::Notification.all 34 | resp.all? { |r| r.is_a? Twilio::Notification }.should be_true 35 | end 36 | 37 | JSON.parse(canned_response('list_notifications'))['notifications'].each_with_index do |obj,i| 38 | obj.each do |attr, value| 39 | specify { Twilio::Notification.all[i].send(attr).should == value } 40 | end 41 | end 42 | 43 | it 'accepts options to refine the search' do 44 | query = '.json?Log=0&MessageDate<=2010-12-12&MessageDate>=2010-11-12&Page=5' 45 | stub_request(:get, resource_uri + query). 46 | to_return :body => canned_response('list_notifications'), :status => 200 47 | Twilio::Notification.all :page => 5, :log => '0', :created_before => Date.parse('2010-12-12'), :created_after => Date.parse('2010-11-12') 48 | a_request(:get, resource_uri + query).should have_been_made 49 | end 50 | 51 | context 'on a subaccount' do 52 | before { stub_api_call 'list_notifications', 'SUBACCOUNT_SID' } 53 | 54 | context 'found by passing in an account_sid' do 55 | it 'uses the subaccount sid in the request' do 56 | Twilio::Notification.all :account_sid => 'SUBACCOUNT_SID' 57 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 58 | end 59 | end 60 | 61 | context 'found by passing in an instance of Twilio::Account' do 62 | it 'uses the subaccount sid in the request' do 63 | Twilio::Notification.all :account => mock(:sid => 'SUBACCOUNT_SID') 64 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 65 | end 66 | end 67 | end 68 | end 69 | 70 | describe '.count' do 71 | context 'using a twilio connect subaccount' do 72 | it 'uses the account sid as the username for basic auth' do 73 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 74 | to_return :body => canned_response('list_connect_notifications'), :status => 200 75 | Twilio::Notification.count :account_sid => 'AC0000000000000000000000000000', :connect => true 76 | end 77 | end 78 | 79 | before { stub_api_call 'list_notifications' } 80 | it 'returns the number of resources' do 81 | Twilio::Notification.count.should == 1224 82 | end 83 | 84 | it 'accepts options to refine the search' do 85 | query = '.json?FriendlyName=example&PhoneNumber=2125550000' 86 | stub_request(:get, resource_uri + query). 87 | to_return :body => canned_response('list_notifications'), :status => 200 88 | Twilio::Notification.count :phone_number => '2125550000', :friendly_name => 'example' 89 | a_request(:get, resource_uri + query).should have_been_made 90 | end 91 | 92 | context 'on a subaccount' do 93 | before { stub_api_call 'list_notifications', 'SUBACCOUNT_SID' } 94 | 95 | context 'found by passing in an account_sid' do 96 | it 'uses the subaccount sid in the request' do 97 | Twilio::Notification.count :account_sid => 'SUBACCOUNT_SID' 98 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 99 | end 100 | end 101 | 102 | context 'found by passing in an instance of Twilio::Account' do 103 | it 'uses the subaccount sid in the request' do 104 | Twilio::Notification.count :account => mock(:sid => 'SUBACCOUNT_SID') 105 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 106 | end 107 | end 108 | end 109 | end 110 | 111 | describe '.find' do 112 | context 'using a twilio connect subaccount' do 113 | it 'uses the account sid as the username for basic auth' do 114 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '/NO5a7a84730f529f0a76b3e30c01315d1a.json' ). 115 | to_return :body => canned_response('connect_notification'), :status => 200 116 | Twilio::Notification.find 'NO5a7a84730f529f0a76b3e30c01315d1a', :account_sid => 'AC0000000000000000000000000000', :connect => true 117 | end 118 | end 119 | 120 | context 'on a subaccount' do 121 | before do 122 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + '/NO5a7a84730f529f0a76b3e30c01315d1a' + '.json'). 123 | to_return :body => canned_response('notification'), :status => 200 124 | end 125 | 126 | context 'found by passing in an account_sid' do 127 | it 'uses the subaccount sid in the request' do 128 | Twilio::Notification.find 'NO5a7a84730f529f0a76b3e30c01315d1a', :account_sid => 'SUBACCOUNT_SID' 129 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/NO5a7a84730f529f0a76b3e30c01315d1a' + '.json'). 130 | should have_been_made 131 | end 132 | end 133 | 134 | context 'found by passing in an instance of Twilio::Account' do 135 | it 'uses the subaccount sid in the request' do 136 | Twilio::Notification.find 'NO5a7a84730f529f0a76b3e30c01315d1a', :account => mock(:sid => 'SUBACCOUNT_SID') 137 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/NO5a7a84730f529f0a76b3e30c01315d1a' + '.json'). 138 | should have_been_made 139 | end 140 | end 141 | end 142 | 143 | context 'for a valid notification' do 144 | before do 145 | stub_request(:get, resource_uri + '/NO5a7a84730f529f0a76b3e30c01315d1a' + '.json'). 146 | to_return :body => canned_response('notification'), :status => 200 147 | end 148 | let(:notification) { Twilio::Notification.find 'NO5a7a84730f529f0a76b3e30c01315d1a' } 149 | 150 | it 'returns an instance of Twilio::Notification.all' do 151 | notification.should be_a Twilio::Notification 152 | end 153 | 154 | JSON.parse(canned_response('notification')).each do |k,v| 155 | specify { notification.send(k).should == v } 156 | end 157 | end 158 | 159 | context 'for a string that does not correspond to a real notification' do 160 | before do 161 | stub_request(:get, resource_uri + '/phony' + '.json').to_return :status => 404 162 | end 163 | it 'returns nil' do 164 | notification = Twilio::Notification.find 'phony' 165 | notification.should be_nil 166 | end 167 | end 168 | end 169 | 170 | describe '#destroy' do 171 | context 'using a twilio connect subaccount' do 172 | it 'uses the account sid as the username for basic auth' do 173 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '/NO5a7a84730f529f0a76b3e30c01315d1a.json' ). 174 | to_return :body => canned_response('connect_notification'), :status => 200 175 | notification = Twilio::Notification.find 'NO5a7a84730f529f0a76b3e30c01315d1a', :account_sid => 'AC0000000000000000000000000000', :connect => true 176 | stub_request(:delete, resource_uri('AC0000000000000000000000000000', true) + '/' + notification.sid + '.json' ) 177 | notification.destroy 178 | a_request(:delete, resource_uri('AC0000000000000000000000000000', true) + '/' + notification.sid + '.json' ).should have_been_made 179 | end 180 | end 181 | 182 | before do 183 | stub_request(:get, resource_uri + '/NO5a7a84730f529f0a76b3e30c01315d1a' + '.json'). 184 | to_return :body => canned_response('notification'), :status => 200 185 | stub_request(:delete, resource_uri + '/NO5a7a84730f529f0a76b3e30c01315d1a' + '.json'). 186 | to_return :status => 204 187 | end 188 | 189 | let(:notification) { Twilio::Notification.find 'NO5a7a84730f529f0a76b3e30c01315d1a' } 190 | 191 | it 'deletes the resource' do 192 | notification.destroy 193 | a_request(:delete, resource_uri + '/NO5a7a84730f529f0a76b3e30c01315d1a' + '.json'). 194 | should have_been_made 195 | end 196 | 197 | it 'freezes itself if successful' do 198 | notification.destroy 199 | notification.should be_frozen 200 | end 201 | 202 | context 'when the participant has already been kicked' do 203 | it 'raises a RuntimeError' do 204 | notification.destroy 205 | lambda { notification.destroy }.should raise_error(RuntimeError, 'Notification has already been destroyed') 206 | end 207 | end 208 | end 209 | end 210 | -------------------------------------------------------------------------------- /spec/outgoing_caller_id_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Twilio::OutgoingCallerId do 4 | 5 | before { Twilio::Config.setup :account_sid => 'AC228ba7a5fe4238be081ea6f3c44186f3', :auth_token => '79ad98413d911947f0ba369d295ae7a3' } 6 | let(:params) { { :phone_number => '+19175551234', :friendly_name => 'barry' } } 7 | let(:post_body) { 'PhoneNumber=%2B19175551234&FriendlyName=barry'} 8 | 9 | def resource_uri(account_sid=nil, connect=nil) 10 | account_sid ||= Twilio::Config.account_sid 11 | "https://#{connect ? account_sid : Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts/#{account_sid}/OutgoingCallerIds" 12 | end 13 | 14 | def stub_api_call(response_file, account_sid=nil) 15 | stub_request(:get, resource_uri(account_sid) + '.json'). 16 | to_return :body => canned_response(response_file), :status => 200 17 | end 18 | 19 | describe '.all' do 20 | context 'using a twilio connect subaccount' do 21 | it 'uses the account sid as the username for basic auth' do 22 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 23 | to_return :body => canned_response('list_connect_caller_ids'), :status => 200 24 | Twilio::OutgoingCallerId.all :account_sid => 'AC0000000000000000000000000000', :connect => true 25 | end 26 | end 27 | 28 | before { stub_api_call 'list_caller_ids' } 29 | it 'returns a collection of objects with a length corresponding to the response' do 30 | resp = Twilio::OutgoingCallerId.all 31 | resp.length.should == 1 32 | end 33 | 34 | it 'returns a collection containing instances of Twilio::OutgoingCallerId' do 35 | resp = Twilio::OutgoingCallerId.all 36 | resp.all? { |r| r.is_a? Twilio::OutgoingCallerId }.should be_true 37 | end 38 | 39 | JSON.parse(canned_response('list_caller_ids'))['outgoing_caller_ids'].each_with_index do |obj,i| 40 | obj.each do |attr, value| 41 | specify { Twilio::OutgoingCallerId.all[i].send(attr).should == value } 42 | end 43 | end 44 | 45 | it 'accepts options to refine the search' do 46 | query = '.json?FriendlyName=barry&Page=5&PhoneNumber=%2B19175551234' 47 | stub_request(:get, resource_uri + query). 48 | to_return :body => canned_response('list_caller_ids'), :status => 200 49 | Twilio::OutgoingCallerId.all :page => 5, :phone_number => '+19175551234', :friendly_name => 'barry' 50 | a_request(:get, resource_uri + query).should have_been_made 51 | end 52 | 53 | context 'on a subaccount' do 54 | before { stub_api_call 'list_caller_ids', 'SUBACCOUNT_SID' } 55 | 56 | context 'found by passing in an account_sid' do 57 | it 'uses the subaccount sid in the request' do 58 | Twilio::OutgoingCallerId.all :account_sid => 'SUBACCOUNT_SID' 59 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 60 | end 61 | end 62 | 63 | context 'found by passing in an instance of Twilio::Account' do 64 | it 'uses the subaccount sid in the request' do 65 | Twilio::OutgoingCallerId.all :account => mock(:sid => 'SUBACCOUNT_SID') 66 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 67 | end 68 | end 69 | end 70 | end 71 | 72 | describe '.count' do 73 | context 'using a twilio connect subaccount' do 74 | it 'uses the account sid as the username for basic auth' do 75 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 76 | to_return :body => canned_response('list_connect_caller_ids'), :status => 200 77 | Twilio::OutgoingCallerId.count :account_sid => 'AC0000000000000000000000000000', :connect => true 78 | end 79 | end 80 | 81 | before { stub_api_call 'list_caller_ids' } 82 | it 'returns the number of resources' do 83 | Twilio::OutgoingCallerId.count.should == 1 84 | end 85 | 86 | it 'accepts options to refine the search' do 87 | query = '.json?FriendlyName=example&PhoneNumber=2125550000' 88 | stub_request(:get, resource_uri + query). 89 | to_return :body => canned_response('list_caller_ids'), :status => 200 90 | Twilio::OutgoingCallerId.count :phone_number => '2125550000', :friendly_name => 'example' 91 | a_request(:get, resource_uri + query).should have_been_made 92 | end 93 | 94 | context 'on a subaccount' do 95 | before { stub_api_call 'list_caller_ids', 'SUBACCOUNT_SID' } 96 | 97 | context 'found by passing in an account_sid' do 98 | it 'uses the subaccount sid in the request' do 99 | Twilio::OutgoingCallerId.count :account_sid => 'SUBACCOUNT_SID' 100 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 101 | end 102 | end 103 | 104 | context 'found by passing in an instance of Twilio::Account' do 105 | it 'uses the subaccount sid in the request' do 106 | Twilio::OutgoingCallerId.count :account => mock(:sid => 'SUBACCOUNT_SID') 107 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 108 | end 109 | end 110 | end 111 | end 112 | 113 | describe '.find' do 114 | context 'using a twilio connect subaccount' do 115 | it 'uses the account sid as the username for basic auth' do 116 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '/PNe905d7e6b410746a0fb08c57e5a186f3.json' ). 117 | to_return :body => canned_response('list_connect_caller_ids'), :status => 200 118 | Twilio::OutgoingCallerId.find 'PNe905d7e6b410746a0fb08c57e5a186f3', :account_sid => 'AC0000000000000000000000000000', :connect => true 119 | end 120 | end 121 | 122 | context 'for a valid caller_id' do 123 | before do 124 | stub_request(:get, resource_uri + '/PNe905d7e6b410746a0fb08c57e5a186f3' + '.json'). 125 | to_return :body => canned_response('caller_id'), :status => 200 126 | end 127 | let(:caller_id) { Twilio::OutgoingCallerId.find 'PNe905d7e6b410746a0fb08c57e5a186f3' } 128 | 129 | it 'returns an instance of Twilio::OutgoingCallerId.all' do 130 | caller_id.should be_a Twilio::OutgoingCallerId 131 | end 132 | 133 | JSON.parse(canned_response('caller_id')).each do |k,v| 134 | specify { caller_id.send(k).should == v } 135 | end 136 | end 137 | 138 | context 'for a string that does not correspond to a real caller_id' do 139 | before do 140 | stub_request(:get, resource_uri + '/phony' + '.json').to_return :status => 404 141 | end 142 | it 'returns nil' do 143 | caller_id = Twilio::OutgoingCallerId.find 'phony' 144 | caller_id.should be_nil 145 | end 146 | end 147 | 148 | context 'on a subaccount' do 149 | before do 150 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + '/PNe905d7e6b410746a0fb08c57e5a186f3' + '.json'). 151 | to_return :body => canned_response('caller_id'), :status => 200 152 | end 153 | 154 | context 'found by passing in an account_sid' do 155 | it 'uses the subaccount sid in the request' do 156 | Twilio::OutgoingCallerId.find 'PNe905d7e6b410746a0fb08c57e5a186f3', :account_sid => 'SUBACCOUNT_SID' 157 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/PNe905d7e6b410746a0fb08c57e5a186f3' + '.json'). 158 | should have_been_made 159 | end 160 | end 161 | 162 | context 'found by passing in an instance of Twilio::Account' do 163 | it 'uses the subaccount sid in the request' do 164 | Twilio::OutgoingCallerId.find 'PNe905d7e6b410746a0fb08c57e5a186f3', :account => mock(:sid => 'SUBACCOUNT_SID') 165 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/PNe905d7e6b410746a0fb08c57e5a186f3' + '.json'). 166 | should have_been_made 167 | end 168 | end 169 | end 170 | end 171 | 172 | describe '.create' do 173 | context 'using a twilio connect subaccount' do 174 | it 'uses the account sid as the username for basic auth' do 175 | stub_request(:post, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 176 | with(:body => post_body). 177 | to_return :body => canned_response('connect_caller_id'), :status => 200 178 | Twilio::OutgoingCallerId.create params.merge(:account_sid => 'AC0000000000000000000000000000', :connect => true) 179 | end 180 | end 181 | context 'on the main account' do 182 | before { stub_request(:post, resource_uri + '.json').with(:body => post_body).to_return :body => canned_response('caller_id')} 183 | let(:caller_id) { Twilio::OutgoingCallerId.create params } 184 | 185 | it 'creates a new incoming caller_id on the account' do 186 | caller_id 187 | a_request(:post, resource_uri + '.json').with(:body => post_body).should have_been_made 188 | end 189 | 190 | it 'returns an instance of Twilio::OutgoingCallerId' do 191 | caller_id.should be_a Twilio::OutgoingCallerId 192 | end 193 | 194 | JSON.parse(canned_response('caller_id')).map do |k,v| 195 | specify { caller_id.send(k).should == v } 196 | end 197 | end 198 | 199 | context 'on a subaccount' do 200 | context 'found by passing in a account sid string' do 201 | before do 202 | stub_request(:post, resource_uri('SUBACCOUNT_SID') + '.json').with(:body => post_body).to_return :body => canned_response('caller_id') 203 | end 204 | 205 | let(:caller_id) { Twilio::OutgoingCallerId.create params.merge(:account_sid => 'SUBACCOUNT_SID') } 206 | 207 | it 'creates a new incoming caller_id on the account' do 208 | caller_id 209 | a_request(:post, resource_uri('SUBACCOUNT_SID') + '.json').with(:body => post_body).should have_been_made 210 | end 211 | 212 | it 'returns an instance of Twilio::OutgoingCallerId' do 213 | caller_id.should be_a Twilio::OutgoingCallerId 214 | end 215 | 216 | JSON.parse(canned_response('caller_id')).map do |k,v| 217 | specify { caller_id.send(k).should == v } 218 | end 219 | end 220 | 221 | context 'found by passing in an actual instance of Twilio::Account' do 222 | before do 223 | stub_request(:post, resource_uri('SUBACCOUNT_SID') + '.json').with(:body => post_body).to_return :body => canned_response('caller_id') 224 | end 225 | 226 | let(:caller_id) { Twilio::OutgoingCallerId.create params.merge(:account => mock(:sid => 'SUBACCOUNT_SID')) } 227 | 228 | it 'creates a new incoming caller_id on the account' do 229 | caller_id 230 | a_request(:post, resource_uri('SUBACCOUNT_SID') + '.json').with(:body => post_body).should have_been_made 231 | end 232 | 233 | it 'returns an instance of Twilio::OutgoingCallerId' do 234 | caller_id.should be_a Twilio::OutgoingCallerId 235 | end 236 | 237 | JSON.parse(canned_response('caller_id')).map do |k,v| 238 | specify { caller_id.send(k).should == v } 239 | end 240 | end 241 | end 242 | end 243 | 244 | describe '#destroy' do 245 | context 'using a twilio connect subaccount' do 246 | it 'uses the account sid as the username for basic auth' do 247 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '/PNe905d7e6b410746a0fb08c57e5a186f3.json' ). 248 | to_return :body => canned_response('connect_caller_id'), :status => 200 249 | caller_id = Twilio::OutgoingCallerId.find 'PNe905d7e6b410746a0fb08c57e5a186f3', :account_sid => 'AC0000000000000000000000000000', :connect => true 250 | stub_request(:delete, resource_uri('AC0000000000000000000000000000', true) + '/' + caller_id.sid + '.json' ) 251 | caller_id.destroy 252 | a_request(:delete, resource_uri('AC0000000000000000000000000000', true) + '/' + caller_id.sid + '.json' ).should have_been_made 253 | end 254 | end 255 | 256 | before do 257 | stub_request(:get, resource_uri + '/PNe905d7e6b410746a0fb08c57e5a186f3' + '.json'). 258 | to_return :body => canned_response('caller_id'), :status => 200 259 | stub_request(:delete, resource_uri + '/PNe905d7e6b410746a0fb08c57e5a186f3' + '.json'). 260 | to_return :status => 204 261 | end 262 | 263 | let(:caller_id) { Twilio::OutgoingCallerId.find 'PNe905d7e6b410746a0fb08c57e5a186f3' } 264 | 265 | it 'deletes the resource' do 266 | caller_id.destroy 267 | a_request(:delete, resource_uri + '/PNe905d7e6b410746a0fb08c57e5a186f3' + '.json'). 268 | should have_been_made 269 | end 270 | 271 | it 'freezes itself if successful' do 272 | caller_id.destroy 273 | caller_id.should be_frozen 274 | end 275 | 276 | context 'when the participant has already been kicked' do 277 | it 'raises a RuntimeError' do 278 | caller_id.destroy 279 | lambda { caller_id.destroy }.should raise_error(RuntimeError, 'OutgoingCallerId has already been destroyed') 280 | end 281 | end 282 | end 283 | 284 | describe '#update_attributes' do 285 | let(:caller_id) { Twilio::OutgoingCallerId.create params } 286 | 287 | 288 | context 'using a twilio connect subaccount' do 289 | it 'uses the account sid for basic auth' do 290 | stub_request(:post, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 291 | with(:body => post_body). 292 | to_return :body => canned_response('connect_caller_id'), :status => 200 293 | caller_id = Twilio::OutgoingCallerId.create params.merge :account_sid => 'AC0000000000000000000000000000', :connect => true 294 | 295 | stub_request(:post, resource_uri('AC0000000000000000000000000000', true) + '/' + caller_id.sid + '.json' ). 296 | with(:body => 'FriendlyName=foo'). 297 | to_return :body => canned_response('connect_caller_id'), :status => 200 298 | 299 | caller_id.update_attributes :friendly_name => 'foo' 300 | 301 | a_request(:post, resource_uri('AC0000000000000000000000000000', true) + '/' + caller_id.sid + '.json' ). 302 | with(:body => 'FriendlyName=foo'). 303 | should have_been_made 304 | 305 | end 306 | end 307 | 308 | before do 309 | stub_request(:post, resource_uri + '.json').with(:body => post_body). 310 | to_return :body => canned_response('caller_id') 311 | stub_request(:post, resource_uri + '/' + caller_id.sid + '.json').with(params). 312 | to_return :body => canned_response('caller_id') 313 | end 314 | context 'when the resource has been persisted' do 315 | it 'updates the API number the new parameters' do 316 | caller_id.update_attributes :url => 'http://localhost:3000/hollaback' 317 | a_request(:post, resource_uri + '/' + caller_id.sid + '.json').with(params).should have_been_made 318 | end 319 | end 320 | end 321 | 322 | describe '#friendly_name=' do 323 | let(:caller_id) { Twilio::OutgoingCallerId.create params } 324 | 325 | before do 326 | stub_request(:post, resource_uri + '.json').with(:body => post_body). 327 | to_return :body => canned_response('caller_id') 328 | end 329 | 330 | it 'updates the friendly_name property with the API' do 331 | stub_request(:post, resource_uri + '/' + caller_id.sid + '.json'). 332 | with(:body => "FriendlyName=foo").to_return :body => canned_response('caller_id'), :status => 201 333 | caller_id.friendly_name = 'foo' 334 | a_request(:post, resource_uri + '/' + caller_id.sid + '.json').with(:body => "FriendlyName=foo").should have_been_made 335 | end 336 | end 337 | end 338 | -------------------------------------------------------------------------------- /spec/participant_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Twilio::Participant do 4 | 5 | def resource_uri(account_sid=nil, connect=nil) 6 | account_sid ||= Twilio::Config.account_sid 7 | "https://#{connect ? account_sid : Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts/#{account_sid}" + 8 | "/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants/CA386025c9bf5d6052a1d1ea42b4d16662.json" 9 | end 10 | 11 | let(:participant) do 12 | Twilio::Participant.new JSON.parse(canned_response('show_participant')) 13 | end 14 | 15 | before { Twilio::Config.setup :account_sid => 'AC5ef872f6da5a21de157d80997a64bd33', :auth_token => '79ad98413d911947f0ba369d295ae7a3' } 16 | 17 | def stub_api_call(meth, response_file) 18 | stub_request(meth, resource_uri). 19 | to_return :body => canned_response(response_file), :status => 200 20 | end 21 | 22 | describe '#destroy' do 23 | context 'using a twilio connect subaccount' do 24 | it 'uses the account sid as the username for basic auth' do 25 | participant = Twilio::Participant.new JSON.parse(canned_response('show_connect_participant')) 26 | stub_request(:delete, resource_uri('AC0000000000000000000000000000', true)) 27 | participant.destroy 28 | a_request(:delete, resource_uri('AC0000000000000000000000000000', true)) 29 | end 30 | end 31 | 32 | before { stub_request(:delete, resource_uri).to_return :status => 204 } 33 | it 'sends a HTTP delete request' do 34 | participant.kick! 35 | a_request(:delete, resource_uri).should have_been_made 36 | end 37 | it 'freezes itself if successful' do 38 | participant.kick! 39 | participant.should be_frozen 40 | end 41 | context 'when the participant has already been kicked' do 42 | it 'raises a RuntimeError' do 43 | participant.freeze 44 | lambda { participant.kick! }.should raise_error(RuntimeError, 'Participant has already been destroyed') 45 | end 46 | end 47 | end 48 | 49 | describe '#kick!' do 50 | it 'is an alias of destroy' do 51 | participant.method(:kick!).should === participant.method(:destroy) 52 | end 53 | end 54 | 55 | describe '#update_attributes=' do 56 | context 'using a twilio connect subaccount' do 57 | it 'uses the account sid for basic auth' do 58 | participant = Twilio::Participant.new JSON.parse(canned_response('show_connect_participant')) 59 | 60 | stub_request(:post, resource_uri('AC0000000000000000000000000000', true)). 61 | with(:body => 'Muted=false'). 62 | to_return :body => canned_response('connect_call_created'), :status => 200 63 | 64 | participant.update_attributes :muted => false 65 | 66 | a_request(:post, resource_uri('AC0000000000000000000000000000', true)). 67 | with(:body => 'Muted=false'). 68 | should have_been_made 69 | 70 | end 71 | end 72 | end 73 | 74 | describe '#muted=' do 75 | it 'makes an api call with the new value' do 76 | stub_request(:post, resource_uri).to_return :status => 201, :body => canned_response('muted_participant') 77 | participant.muted = true 78 | a_request(:post, resource_uri).with(:body => 'Muted=true').should have_been_made 79 | end 80 | end 81 | 82 | describe '#mute!' do 83 | context 'when the participant is unmuted' do 84 | before { stub_request(:post, resource_uri).to_return :status => 201, :body => canned_response('muted_participant') } 85 | it "mutes the participant" do 86 | participant.mute! 87 | participant.should be_muted 88 | a_request(:post, resource_uri).with(:body => 'Muted=true').should have_been_made 89 | end 90 | end 91 | context 'when the participant is muted' do 92 | before { stub_request(:post, resource_uri).to_return :status => 201, :body => canned_response('show_participant') } 93 | 94 | let(:participant) do 95 | Twilio::Participant.new JSON.parse(canned_response('muted_participant')) 96 | end 97 | 98 | it "unmutes the participant" do 99 | participant.mute! 100 | participant.should_not be_muted 101 | a_request(:post, resource_uri).with(:body => 'Muted=false').should have_been_made 102 | end 103 | end 104 | context 'when the participant has been kicked' do 105 | it 'raises a RuntimeError' do 106 | participant.freeze 107 | lambda { participant.mute! }.should raise_error(RuntimeError, 'Participant has already been destroyed') 108 | end 109 | end 110 | end 111 | end 112 | -------------------------------------------------------------------------------- /spec/queue_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Twilio::Queue do 4 | 5 | before { Twilio::Config.setup :account_sid => 'ACdc5f1e11047ebd6fe7a55f120be3a900', :auth_token => '79ad98413d911947f0ba369d295ae7a3' } 6 | 7 | def resource_uri(account_sid=nil, connect=nil) 8 | account_sid ||= Twilio::Config.account_sid 9 | "https://#{connect ? account_sid : Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts/#{account_sid}/Queues" 10 | end 11 | 12 | def stub_api_call(response_file, account_sid=nil) 13 | stub_request(:get, resource_uri(account_sid) + '.json'). 14 | to_return :body => canned_response(response_file), :status => 200 15 | end 16 | 17 | let(:params) do 18 | { :friendly_name => "switchboard", :max_size => 40 } 19 | end 20 | 21 | let(:queue) { Twilio::Queue.create params } 22 | 23 | describe '.all' do 24 | before { stub_api_call 'list_queues' } 25 | let (:resp) { Twilio::Queue.all } 26 | 27 | it 'returns a collection of objects with a length corresponding to the response' do 28 | resp.length.should == 2 29 | end 30 | 31 | it 'returns a collection containing instances of Twilio::Queue' do 32 | resp.all? { |r| r.is_a? Twilio::Queue }.should be_true 33 | end 34 | 35 | JSON.parse(canned_response('list_queues'))['queues'].each_with_index do |obj,i| 36 | obj.each do |attr, value| 37 | specify { resp[i].send(attr).should == value } 38 | end 39 | end 40 | 41 | context 'on a subaccount' do 42 | before { stub_api_call 'list_queues', 'SUBACCOUNT_SID' } 43 | 44 | context 'found by passing in an account_sid' do 45 | it 'uses the subaccount sid in the request' do 46 | Twilio::Queue.all :account_sid => 'SUBACCOUNT_SID' 47 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 48 | end 49 | end 50 | 51 | context 'found by passing in an instance of Twilio::Account' do 52 | it 'uses the subaccount sid in the request' do 53 | Twilio::Queue.all :account => mock(:sid => 'SUBACCOUNT_SID') 54 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 55 | end 56 | end 57 | end 58 | end 59 | 60 | describe '.count' do 61 | context 'using a twilio connect subaccount' do 62 | it 'uses the account sid as the username for basic auth' do 63 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 64 | to_return :body => canned_response('list_queues'), :status => 200 65 | Twilio::Queue.count :account_sid => 'AC0000000000000000000000000000', :connect => true 66 | end 67 | end 68 | 69 | before { stub_api_call 'list_queues' } 70 | it 'returns the number of resources' do 71 | Twilio::Queue.count.should == 2 72 | end 73 | 74 | context 'on a subaccount' do 75 | before { stub_api_call 'list_queues', 'SUBACCOUNT_SID' } 76 | 77 | context 'found by passing in an account_sid' do 78 | it 'uses the subaccount sid in the request' do 79 | Twilio::Queue.count :account_sid => 'SUBACCOUNT_SID' 80 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 81 | end 82 | end 83 | 84 | context 'found by passing in an instance of Twilio::Account' do 85 | it 'uses the subaccount sid in the request' do 86 | Twilio::Queue.count :account => mock(:sid => 'SUBACCOUNT_SID') 87 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 88 | end 89 | end 90 | end 91 | end 92 | 93 | describe '.find' do 94 | context 'using a twilio connect subaccount' do 95 | it 'uses the account sid as the username for basic auth' do 96 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '/QU14fbe51706f6ef1e4756afXXbed88055.json' ). 97 | to_return :body => canned_response('list_queues'), :status => 200 98 | Twilio::Queue.find 'QU14fbe51706f6ef1e4756afXXbed88055', :account_sid => 'AC0000000000000000000000000000', :connect => true 99 | end 100 | end 101 | 102 | context 'for a valid queue' do 103 | before do 104 | stub_request(:get, resource_uri + '/QU14fbe51706f6ef1e4756afXXbed88055' + '.json'). 105 | to_return :body => canned_response('queue'), :status => 200 106 | end 107 | 108 | let(:queue) { Twilio::Queue.find 'QU14fbe51706f6ef1e4756afXXbed88055' } 109 | 110 | it 'returns an instance of Twilio::Queue.all' do 111 | queue.should be_a Twilio::Queue 112 | end 113 | 114 | JSON.parse(canned_response('queue')).each do |k,v| 115 | specify { queue.send(k).should == v } 116 | end 117 | end 118 | 119 | context 'for a string that does not correspond to a real queue' do 120 | before do 121 | stub_request(:get, resource_uri + '/phony' + '.json').to_return :status => 404 122 | end 123 | it 'returns nil' do 124 | queue = Twilio::Queue.find 'phony' 125 | queue.should be_nil 126 | end 127 | end 128 | 129 | context 'on a subaccount' do 130 | before do 131 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + '/QU14fbe51706f6ef1e4756afXXbed88055' + '.json'). 132 | to_return :body => canned_response('notification'), :status => 200 133 | end 134 | 135 | context 'found by passing in an account_sid' do 136 | it 'uses the subaccount sid in the request' do 137 | Twilio::Queue.find 'QU14fbe51706f6ef1e4756afXXbed88055', :account_sid => 'SUBACCOUNT_SID' 138 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/QU14fbe51706f6ef1e4756afXXbed88055' + '.json'). 139 | should have_been_made 140 | end 141 | end 142 | 143 | context 'found by passing in an instance of Twilio::Account' do 144 | it 'uses the subaccount sid in the request' do 145 | Twilio::Queue.find 'QU14fbe51706f6ef1e4756afXXbed88055', :account => mock(:sid => 'SUBACCOUNT_SID') 146 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/QU14fbe51706f6ef1e4756afXXbed88055' + '.json'). 147 | should have_been_made 148 | end 149 | end 150 | end 151 | end 152 | end 153 | -------------------------------------------------------------------------------- /spec/recording_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Twilio::Recording do 4 | 5 | before { Twilio::Config.setup :account_sid => 'ACda6f1e11047ebd6fe7a55f120be3a900', :auth_token => '79ad98413d911947f0ba369d295ae7a3' } 6 | 7 | def resource_uri(account_sid=nil, connect=nil) 8 | account_sid ||= Twilio::Config.account_sid 9 | "https://#{connect ? account_sid : Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts/#{account_sid}/Recordings" 10 | end 11 | 12 | def stub_api_call(response_file, account_sid=nil) 13 | stub_request(:get, resource_uri(account_sid) + '.json'). 14 | to_return :body => canned_response(response_file), :status => 200 15 | end 16 | 17 | describe '.all' do 18 | context 'using a twilio connect subaccount' do 19 | it 'uses the account sid as the username for basic auth' do 20 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 21 | to_return :body => canned_response('list_connect_recordings'), :status => 200 22 | Twilio::Recording.all :account_sid => 'AC0000000000000000000000000000', :connect => true 23 | end 24 | end 25 | before { stub_api_call 'list_recordings' } 26 | let (:resp) { Twilio::Recording.all } 27 | 28 | it 'returns a collection of objects with a length corresponding to the response' do 29 | resp.length.should == 1 30 | end 31 | 32 | it 'returns a collection containing instances of Twilio::Recording' do 33 | resp.all? { |r| r.is_a? Twilio::Recording }.should be_true 34 | end 35 | 36 | JSON.parse(canned_response('list_recordings'))['recordings'].each_with_index do |obj,i| 37 | obj.each do |attr, value| 38 | specify { resp[i].send(attr).should == value } 39 | end 40 | end 41 | 42 | it 'accepts options to refine the search' do 43 | query = '.json?CallSid=CAa346467ca321c71dbd5e12f627deb854&DateCreated<=2010-12-12&DateCreated>=2010-11-12&Page=5' 44 | stub_request(:get, resource_uri + query). 45 | to_return :body => canned_response('list_recordings'), :status => 200 46 | Twilio::Recording.all :page => 5, :call_sid => 'CAa346467ca321c71dbd5e12f627deb854', :created_before => Date.parse('2010-12-12'), :created_after => Date.parse('2010-11-12') 47 | a_request(:get, resource_uri + query).should have_been_made 48 | end 49 | 50 | context 'on a subaccount' do 51 | before { stub_api_call 'list_recordings', 'SUBACCOUNT_SID' } 52 | 53 | context 'found by passing in an account_sid' do 54 | it 'uses the subaccount sid in the request' do 55 | Twilio::Recording.all :account_sid => 'SUBACCOUNT_SID' 56 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 57 | end 58 | end 59 | 60 | context 'found by passing in an instance of Twilio::Account' do 61 | it 'uses the subaccount sid in the request' do 62 | Twilio::Recording.all :account => mock(:sid => 'SUBACCOUNT_SID') 63 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 64 | end 65 | end 66 | end 67 | end 68 | 69 | describe '.count' do 70 | context 'using a twilio connect subaccount' do 71 | it 'uses the account sid as the username for basic auth' do 72 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 73 | to_return :body => canned_response('list_connect_recordings'), :status => 200 74 | Twilio::Recording.count :account_sid => 'AC0000000000000000000000000000', :connect => true 75 | end 76 | end 77 | 78 | before { stub_api_call 'list_recordings' } 79 | it 'returns the number of resources' do 80 | Twilio::Recording.count.should == 527 81 | end 82 | 83 | it 'accepts options to refine the search' do 84 | query = '.json?CallSid=CAa346467ca321c71dbd5e12f627deb854&DateCreated<=2010-12-12' 85 | stub_request(:get, resource_uri + query). 86 | to_return :body => canned_response('list_recordings'), :status => 200 87 | Twilio::Recording.count :call_sid => 'CAa346467ca321c71dbd5e12f627deb854', :created_before => Date.parse('2010-12-12') 88 | a_request(:get, resource_uri + query).should have_been_made 89 | end 90 | 91 | context 'on a subaccount' do 92 | before { stub_api_call 'list_recordings', 'SUBACCOUNT_SID' } 93 | 94 | context 'found by passing in an account_sid' do 95 | it 'uses the subaccount sid in the request' do 96 | Twilio::Recording.count :account_sid => 'SUBACCOUNT_SID' 97 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 98 | end 99 | end 100 | 101 | context 'found by passing in an instance of Twilio::Account' do 102 | it 'uses the subaccount sid in the request' do 103 | Twilio::Recording.count :account => mock(:sid => 'SUBACCOUNT_SID') 104 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 105 | end 106 | end 107 | end 108 | end 109 | 110 | describe '.find' do 111 | context 'using a twilio connect subaccount' do 112 | it 'uses the account sid as the username for basic auth' do 113 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '/RE557ce644e5ab84fa21cc21112e22c485.json' ). 114 | to_return :body => canned_response('list_connect_recordings'), :status => 200 115 | Twilio::Recording.find 'RE557ce644e5ab84fa21cc21112e22c485', :account_sid => 'AC0000000000000000000000000000', :connect => true 116 | end 117 | end 118 | 119 | context 'for a valid recording' do 120 | before do 121 | stub_request(:get, resource_uri + '/RE557ce644e5ab84fa21cc21112e22c485' + '.json'). 122 | to_return :body => canned_response('recording'), :status => 200 123 | end 124 | 125 | let(:recording) { Twilio::Recording.find 'RE557ce644e5ab84fa21cc21112e22c485' } 126 | 127 | it 'returns an instance of Twilio::Recording.all' do 128 | recording.should be_a Twilio::Recording 129 | end 130 | 131 | JSON.parse(canned_response('recording')).each do |k,v| 132 | specify { recording.send(k).should == v } 133 | end 134 | end 135 | 136 | context 'for a string that does not correspond to a real recording' do 137 | before do 138 | stub_request(:get, resource_uri + '/phony' + '.json').to_return :status => 404 139 | end 140 | it 'returns nil' do 141 | recording = Twilio::Recording.find 'phony' 142 | recording.should be_nil 143 | end 144 | end 145 | 146 | context 'on a subaccount' do 147 | before do 148 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + '/RE557ce644e5ab84fa21cc21112e22c485' + '.json'). 149 | to_return :body => canned_response('notification'), :status => 200 150 | end 151 | 152 | context 'found by passing in an account_sid' do 153 | it 'uses the subaccount sid in the request' do 154 | Twilio::Recording.find 'RE557ce644e5ab84fa21cc21112e22c485', :account_sid => 'SUBACCOUNT_SID' 155 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/RE557ce644e5ab84fa21cc21112e22c485' + '.json'). 156 | should have_been_made 157 | end 158 | end 159 | 160 | context 'found by passing in an instance of Twilio::Account' do 161 | it 'uses the subaccount sid in the request' do 162 | Twilio::Recording.find 'RE557ce644e5ab84fa21cc21112e22c485', :account => mock(:sid => 'SUBACCOUNT_SID') 163 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/RE557ce644e5ab84fa21cc21112e22c485' + '.json'). 164 | should have_been_made 165 | end 166 | end 167 | end 168 | end 169 | 170 | describe '#destroy' do 171 | context 'using a twilio connect subaccount' do 172 | it 'uses the account sid as the username for basic auth' do 173 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '/RE557ce644e5ab84fa21cc21112e22c485.json' ). 174 | to_return :body => canned_response('connect_recording'), :status => 200 175 | recording = Twilio::Recording.find 'RE557ce644e5ab84fa21cc21112e22c485', :account_sid => 'AC0000000000000000000000000000', :connect => true 176 | stub_request(:delete, resource_uri('AC0000000000000000000000000000', true) + '/' + recording.sid + '.json' ) 177 | recording.destroy 178 | a_request(:delete, resource_uri('AC0000000000000000000000000000', true) + '/' + recording.sid + '.json' ).should have_been_made 179 | end 180 | end 181 | 182 | before do 183 | stub_request(:get, resource_uri + '/RE557ce644e5ab84fa21cc21112e22c485' + '.json'). 184 | to_return :body => canned_response('recording'), :status => 200 185 | stub_request(:delete, resource_uri + '/RE557ce644e5ab84fa21cc21112e22c485' + '.json'). 186 | to_return :status => 204 187 | end 188 | 189 | let(:recording) { Twilio::Recording.find 'RE557ce644e5ab84fa21cc21112e22c485' } 190 | 191 | it 'deletes the resource' do 192 | recording.destroy 193 | a_request(:delete, resource_uri + '/RE557ce644e5ab84fa21cc21112e22c485' + '.json'). 194 | should have_been_made 195 | end 196 | 197 | it 'freezes itself if successful' do 198 | recording.destroy 199 | recording.should be_frozen 200 | end 201 | 202 | context 'when the participant has already been kicked' do 203 | it 'raises a RuntimeError' do 204 | recording.destroy 205 | lambda { recording.destroy }.should raise_error(RuntimeError, 'Recording has already been destroyed') 206 | end 207 | end 208 | end 209 | 210 | describe '#mp3' do 211 | before do 212 | stub_request(:get, resource_uri + '/RE557ce644e5ab84fa21cc21112e22c485' + '.json'). 213 | to_return :body => canned_response('recording'), :status => 200 214 | end 215 | 216 | let(:recording) { Twilio::Recording.find 'RE557ce644e5ab84fa21cc21112e22c485' } 217 | 218 | it 'returns a url to the mp3 file for the recording' do 219 | recording.mp3.should == "https://api.twilio.com/2010-04-01/Accounts/#{recording.account_sid}/Recordings/RE557ce644e5ab84fa21cc21112e22c485.mp3" 220 | end 221 | end 222 | 223 | describe '#wav' do 224 | before do 225 | stub_request(:get, resource_uri + '/RE557ce644e5ab84fa21cc21112e22c485' + '.json'). 226 | to_return :body => canned_response('recording'), :status => 200 227 | end 228 | 229 | let(:recording) { Twilio::Recording.find 'RE557ce644e5ab84fa21cc21112e22c485' } 230 | 231 | it 'returns a url to the wav file for the recording' do 232 | recording.wav.should == "https://api.twilio.com/2010-04-01/Accounts/#{recording.account_sid}/Recordings/RE557ce644e5ab84fa21cc21112e22c485.wav" 233 | end 234 | end 235 | end 236 | -------------------------------------------------------------------------------- /spec/request_filter_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper') 2 | 3 | describe 'Twilio::RequestFilter' do 4 | before { Twilio::Config.setup :account_sid => 'AC000000000000', :auth_token => '1c892n40nd03kdnc0112slzkl3091j20' } 5 | 6 | [:filter, :before].each do |method_name| 7 | 8 | describe ".#{method_name}" do 9 | context 'when request signatures do match' do 10 | it 'does not trigger a 401 response' do 11 | request = stub \ 12 | :request_parameters => { 13 | 'AccountSid' => 'AC9a9f9392lad99kla0sklakjs90j092j3', 'ApiVersion' => '2010-04-01', 14 | 'CallSid' => 'CAd800bb12c0426a7ea4230e492fef2a4f', 'CallStatus' => 'ringing', 15 | 'Called' => '+15306384866', 'CalledCity' => 'OAKLAND', 'CalledCountry' => 'US', 16 | 'CalledState' => 'CA', 'CalledZip' => '94612', 'Caller' => '+15306666666', 17 | 'CallerCity' => 'SOUTH LAKE TAHOE', 'CallerCountry' => 'US', 18 | 'CallerName' => 'CA Wireless Call', 'CallerState' => 'CA', 19 | 'CallerZip' => '89449', 'Direction' => 'inbound', 'From' => '+15306666666', 20 | 'FromCity' => 'SOUTH LAKE TAHOE', 'FromCountry' => 'US', 'FromState' => 'CA', 21 | 'FromZip' => '89449', 'To' => '+15306384866', 'ToCity' => 'OAKLAND', 22 | 'ToCountry' => 'US', 'ToState' => 'CA', 'ToZip' => '94612' }, 23 | :env => { 24 | 'X-Twilio-Signature' => 'fF+xx6dTinOaCdZ0aIeNkHr/ZAA=', 25 | 'HTTP_X_TWILIO_SIGNATURE' => 'fF+xx6dTinOaCdZ0aIeNkHr/ZAA=' }, 26 | :format => mock(:voice? => true), 27 | :url => 'http://www.postbin.org/1ed898x' 28 | 29 | controller = stub :request => request 30 | 31 | controller.expects(:head).with(:forbidden).never 32 | Twilio::RequestFilter.send(method_name, controller) 33 | end 34 | end 35 | 36 | context 'when request signatures do not match' do 37 | it 'returns 401 response' do 38 | request = stub \ 39 | :request_parameters => { 40 | 'AccountSid' => 'AC9a9f9392lad99kla0sklakjs90j092j3', 'ApiVersion' => '2010-04-01', 41 | 'CallSid' => 'CAd800bb12c0426a7ea4230e492fef2a4f', 'CallStatus' => 'ringing', 42 | 'Called' => '+15306384866', 'CalledCity' => 'OAKLAND', 'CalledCountry' => 'US', 43 | 'CalledState' => 'CA', 'CalledZip' => '94612', 'Caller' => '+15306666666', 44 | 'CallerCity' => 'SOUTH LAKE TAHOE', 'CallerCountry' => 'US', 45 | 'CallerName' => 'CA Wireless Call', 'CallerState' => 'CA', 46 | 'CallerZip' => '89449', 'Direction' => 'inbound', 'From' => '+15306666666', 47 | 'FromCity' => 'SOUTH LAKE TAHOE', 'FromCountry' => 'US', 'FromState' => 'CA', 48 | 'FromZip' => '89449', 'To' => '+15306384866', 'ToCity' => 'OAKLAND', 49 | 'ToCountry' => 'US', 'ToState' => 'CA', 'ToZip' => '94612' }, 50 | :env => { 51 | 'X-Twilio-Signature' => nil, 52 | 'HTTP_X_TWILIO_SIGNATURE' => nil }, 53 | :format => mock(:voice? => true), 54 | :url => 'http://www.postbin.org/1ed898x' 55 | 56 | controller = stub :request => request 57 | 58 | controller.expects(:head).with(:forbidden) 59 | Twilio::RequestFilter.send(method_name, controller) 60 | end 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /spec/sandbox_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper') 2 | 3 | describe Twilio::Sandbox do 4 | before do 5 | Twilio::Config.setup :account_sid => 'AC000000000000', :auth_token => '79ad98413d911947f0ba369d295ae7a3' 6 | stub_request(:get, resource).to_return :body => canned_response('sandbox'), :status => 200 7 | end 8 | let(:resource) { "https://#{Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts/#{Twilio::Config.account_sid}/Sandbox.json" } 9 | 10 | describe 'accessing sandbox properties' do 11 | JSON.parse(canned_response('sandbox')). 12 | each { |meth, value| specify { Twilio::Sandbox.send(meth).should == value } } 13 | end 14 | %w.each do |meth| 15 | describe "##{meth}=" do 16 | it "updates the #{meth} property with the API" do 17 | stub_request(:post, resource).with(:body => "#{meth.camelize}=foo").to_return :body => canned_response('sandbox'), :status => 201 18 | Twilio::Sandbox.send "#{meth}=", 'foo' 19 | a_request(:post, resource).with(:body => "#{meth.camelize}=foo").should have_been_made 20 | end 21 | end 22 | end 23 | 24 | describe '#reload!' do 25 | it "makes a request to the API and updates the object's attributes" do 26 | Twilio::Sandbox.reload! 27 | a_request(:get, resource).should have_been_made 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/short_code_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Twilio::ShortCode do 4 | 5 | before { Twilio::Config.setup :account_sid => 'ACdc5f1e6f7a0441659833ca940b72503d', :auth_token => '79ad98413d911947f0ba369d295ae7a3' } 6 | 7 | def resource_uri(account_sid=nil) 8 | account_sid ||= Twilio::Config.account_sid 9 | "https://#{Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts/#{account_sid}/SMS/ShortCodes" 10 | end 11 | 12 | def stub_api_call(response_file, account_sid=nil) 13 | stub_request(:get, resource_uri(account_sid) + '.json'). 14 | to_return :body => canned_response(response_file), :status => 200 15 | end 16 | 17 | describe '.count' do 18 | context 'on the master account' do 19 | before { stub_api_call 'list_short_codes' } 20 | it 'returns the short_code count' do 21 | Twilio::ShortCode.count.should == 10 22 | end 23 | 24 | it 'accepts options to refine the search' do 25 | query = '.json?FriendlyName=example' 26 | stub_request(:get, resource_uri + query). 27 | to_return :body => canned_response('list_short_codes'), :status => 200 28 | Twilio::ShortCode.count :friendly_name => 'example' 29 | a_request(:get, resource_uri + query).should have_been_made 30 | end 31 | end 32 | 33 | context 'on a subaccount' do 34 | context 'found by passing in an account sid' do 35 | before { stub_api_call 'list_short_codes', 'SUBACCOUNT_SID' } 36 | it 'returns the count of short_codes' do 37 | Twilio::ShortCode.count(:account_sid => 'SUBACCOUNT_SID').should == 10 38 | end 39 | 40 | it 'accepts options to refine the search' do 41 | query = '.json?FriendlyName=example' 42 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + query). 43 | to_return :body => canned_response('list_short_codes'), :status => 200 44 | Twilio::ShortCode.count :friendly_name => 'example', :account_sid => 'SUBACCOUNT_SID' 45 | a_request(:get, resource_uri('SUBACCOUNT_SID') + query).should have_been_made 46 | end 47 | end 48 | 49 | context 'found by passing in an instance of Twilio::Account' do 50 | before { stub_api_call 'list_short_codes', 'SUBACCOUNT_SID' } 51 | it 'returns the short_code of resources' do 52 | Twilio::ShortCode.count(:account => mock(:sid => 'SUBACCOUNT_SID')).should == 10 53 | end 54 | 55 | it 'accepts options to refine the search' do 56 | query = '.json?FriendlyName=example' 57 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + query). 58 | to_return :body => canned_response('list_short_codes'), :status => 200 59 | Twilio::ShortCode.count :friendly_name => 'example', :account => mock(:sid => 'SUBACCOUNT_SID') 60 | a_request(:get, resource_uri('SUBACCOUNT_SID') + query).should have_been_made 61 | end 62 | end 63 | end 64 | end 65 | 66 | describe '.all' do 67 | context 'on the master account' do 68 | before { stub_api_call 'list_short_codes' } 69 | let(:resp) { resp = Twilio::ShortCode.all } 70 | it 'returns a collection of objects with a length corresponding to the response' do 71 | resp.length.should == 1 72 | end 73 | 74 | it 'returns a collection containing instances of Twilio::ShortCode' do 75 | resp.all? { |r| r.is_a? Twilio::ShortCode }.should be_true 76 | end 77 | 78 | JSON.parse(canned_response('list_short_codes'))['short_codes'].each_with_index do |obj,i| 79 | obj.each do |attr, value| 80 | specify { resp[i].send(attr).should == value } 81 | end 82 | end 83 | 84 | it 'accepts options to refine the search' do 85 | query = '.json?FriendlyName=example&Page=5' 86 | stub_request(:get, resource_uri + query). 87 | to_return :body => canned_response('list_short_codes'), :status => 200 88 | Twilio::ShortCode.all :page => 5, :friendly_name => 'example' 89 | a_request(:get, resource_uri + query).should have_been_made 90 | end 91 | end 92 | 93 | context 'on a subaccount' do 94 | context 'found by passing in an account sid' do 95 | before { stub_api_call 'list_short_codes', 'SUBACCOUNT_SID' } 96 | let(:resp) { resp = Twilio::ShortCode.all :account_sid => 'SUBACCOUNT_SID' } 97 | it 'returns a collection of objects with a length corresponding to the response' do 98 | resp.length.should == 1 99 | end 100 | 101 | it 'returns a collection containing instances of Twilio::ShortCode' do 102 | resp.all? { |r| r.is_a? Twilio::ShortCode }.should be_true 103 | end 104 | 105 | JSON.parse(canned_response('list_short_codes'))['short_codes'].each_with_index do |obj,i| 106 | obj.each do |attr, value| 107 | specify { resp[i].send(attr).should == value } 108 | end 109 | end 110 | 111 | it 'accepts options to refine the search' do 112 | query = '.json?FriendlyName=example&Page=5' 113 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + query). 114 | to_return :body => canned_response('list_short_codes'), :status => 200 115 | Twilio::ShortCode.all :page => 5, :friendly_name => 'example', :account_sid => 'SUBACCOUNT_SID' 116 | a_request(:get, resource_uri('SUBACCOUNT_SID') + query).should have_been_made 117 | end 118 | end 119 | 120 | context 'found by passing in an instance of Twilio::Account' do 121 | context 'found by passing in an account sid' do 122 | before { stub_api_call 'list_short_codes', 'SUBACCOUNT_SID' } 123 | let(:resp) { resp = Twilio::ShortCode.all :account => mock(:sid =>'SUBACCOUNT_SID') } 124 | it 'returns a collection of objects with a length corresponding to the response' do 125 | resp.length.should == 1 126 | end 127 | 128 | it 'returns a collection containing instances of Twilio::ShortCode' do 129 | resp.all? { |r| r.is_a? Twilio::ShortCode }.should be_true 130 | end 131 | 132 | JSON.parse(canned_response('list_short_codes'))['short_codes'].each_with_index do |obj,i| 133 | obj.each do |attr, value| 134 | specify { resp[i].send(attr).should == value } 135 | end 136 | end 137 | 138 | it 'accepts options to refine the search' do 139 | query = '.json?FriendlyName=example&Page=5' 140 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + query). 141 | to_return :body => canned_response('list_short_codes'), :status => 200 142 | Twilio::ShortCode.all :page => 5, :friendly_name => 'example', :account => mock(:sid =>'SUBACCOUNT_SID') 143 | a_request(:get, resource_uri('SUBACCOUNT_SID') + query).should have_been_made 144 | end 145 | end 146 | end 147 | end 148 | end 149 | 150 | describe '.find' do 151 | context 'on the master account' do 152 | context 'for a valid account' do 153 | before do 154 | stub_request(:get, resource_uri + '/SC6b20cb705c1e8f00210049b20b70fce2' + '.json'). 155 | to_return :body => canned_response('short_code'), :status => 200 156 | end 157 | 158 | let(:short_code) { Twilio::ShortCode.find 'SC6b20cb705c1e8f00210049b20b70fce2' } 159 | 160 | it 'returns an instance of Twilio::ShortCode' do 161 | short_code.should be_a Twilio::ShortCode 162 | end 163 | 164 | JSON.parse(canned_response('short_code')).each do |k,v| 165 | specify { short_code.send(k).should == v } 166 | end 167 | end 168 | 169 | context 'for a string that does not correspond to a real short_code' do 170 | before do 171 | stub_request(:get, resource_uri + '/phony' + '.json').to_return :status => 404 172 | end 173 | it 'returns nil' do 174 | short_code = Twilio::ShortCode.find 'phony' 175 | short_code.should be_nil 176 | end 177 | end 178 | end 179 | 180 | context 'on a subaccount' do 181 | context 'found by passing in an account sid' do 182 | context 'for a valid short_code' do 183 | before do 184 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + '/SC6b20cb705c1e8f00210049b20b70fce2' + '.json'). 185 | to_return :body => canned_response('short_code'), :status => 200 186 | end 187 | 188 | let(:short_code) { Twilio::ShortCode.find 'SC6b20cb705c1e8f00210049b20b70fce2', :account_sid => 'SUBACCOUNT_SID' } 189 | 190 | it 'returns an instance of Twilio::ShortCode' do 191 | short_code.should be_a Twilio::ShortCode 192 | end 193 | 194 | JSON.parse(canned_response('short_code')).each do |k,v| 195 | specify { short_code.send(k).should == v } 196 | end 197 | end 198 | 199 | context 'for a string that does not correspond to a real short_code' do 200 | before do 201 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + '/phony' + '.json').to_return :status => 404 202 | end 203 | it 'returns nil' do 204 | short_code = Twilio::ShortCode.find 'phony', :account_sid => 'SUBACCOUNT_SID' 205 | short_code.should be_nil 206 | end 207 | end 208 | end 209 | 210 | context 'found by passing in an instance of Twilio::Account' do 211 | context 'for a valid short_code' do 212 | before do 213 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + '/SC6b20cb705c1e8f00210049b20b70fce2' + '.json'). 214 | to_return :body => canned_response('short_code'), :status => 200 215 | end 216 | 217 | let(:short_code) do 218 | Twilio::ShortCode.find 'SC6b20cb705c1e8f00210049b20b70fce2', 219 | :account => mock(:sid => 'SUBACCOUNT_SID') 220 | end 221 | 222 | it 'returns an instance of Twilio::ShortCode' do 223 | short_code.should be_a Twilio::ShortCode 224 | end 225 | 226 | JSON.parse(canned_response('short_code')).each do |k,v| 227 | specify { short_code.send(k).should == v } 228 | end 229 | end 230 | 231 | context 'for a string that does not correspond to a real short_code' do 232 | before do 233 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + '/phony' + '.json').to_return :status => 404 234 | end 235 | it 'returns nil' do 236 | short_code = Twilio::ShortCode.find 'phony', :account => mock(:sid => 'SUBACCOUNT_SID') 237 | short_code.should be_nil 238 | end 239 | end 240 | end 241 | end 242 | end 243 | 244 | describe '#update_attributes' do 245 | let(:short_code) { Twilio::ShortCode.find "SC6b20cb705c1e8f00210049b20b70fce2" } 246 | 247 | before do 248 | stub_request(:get, resource_uri + '/SC6b20cb705c1e8f00210049b20b70fce2' + '.json'). 249 | to_return :body => canned_response('short_code'), :status => 200 250 | stub_request(:post, resource_uri + '/' + short_code.sid + '.json'). 251 | with(:body => "Foo=bar").to_return :body => canned_response('short_code'), :status => 201 252 | end 253 | context 'when the resource has been persisted' do 254 | it 'updates the API short_code the new parameters' do 255 | short_code.update_attributes :foo => 'bar' 256 | a_request(:post, resource_uri + '/' + short_code.sid + '.json').with(:body => "Foo=bar").should have_been_made 257 | end 258 | end 259 | end 260 | 261 | %w.each do |meth| 262 | describe "##{meth}=" do 263 | let(:short_code) { Twilio::ShortCode.find "SC6b20cb705c1e8f00210049b20b70fce2" } 264 | 265 | before do 266 | stub_request(:get, resource_uri + '/SC6b20cb705c1e8f00210049b20b70fce2' + '.json'). 267 | to_return :body => canned_response('short_code'), :status => 200 268 | stub_request(:post, resource_uri + '/' + short_code.sid + '.json'). 269 | with(:body => URI.encode("#{meth.camelize}=foo")).to_return :body => canned_response('short_code'), :status => 201 270 | end 271 | 272 | it "updates the #{meth} property with the API" do 273 | short_code.send "#{meth}=", 'foo' 274 | a_request(:post, resource_uri + '/' + short_code.sid + '.json'). 275 | with(:body => URI.encode("#{meth.camelize}=foo")).should have_been_made 276 | end 277 | end 278 | end 279 | end 280 | 281 | 282 | -------------------------------------------------------------------------------- /spec/sms_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper') 2 | 3 | describe Twilio::SMS do 4 | 5 | let(:minimum_sms_params) { 'To=%2B14158141829&From=%2B14159352345&Body=Jenny%20please%3F!%20I%20love%20you%20%3C3' } 6 | let(:sms) { Twilio::SMS.create(:to => '+14158141829', :from => '+14159352345', :body => 'Jenny please?! I love you <3') } 7 | 8 | def resource_uri(account_sid=nil, connect=nil) 9 | account_sid ||= Twilio::Config.account_sid 10 | "https://#{connect ? account_sid : Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts/#{account_sid}/SMS/Messages" 11 | end 12 | 13 | def stub_new_sms 14 | stub_request(:post, resource_uri + '.json').with(:body => minimum_sms_params).to_return :body => canned_response('sms_created'), :status => 201 15 | end 16 | 17 | def new_sms_should_have_been_made 18 | a_request(:post, resource_uri + '.json').with(:body => minimum_sms_params).should have_been_made 19 | end 20 | 21 | before { Twilio::Config.setup :account_sid => 'AC000000000000', :auth_token => '79ad98413d911947f0ba369d295ae7a3' } 22 | 23 | describe '.all' do 24 | before do 25 | stub_request(:get, resource_uri + '.json'). 26 | to_return :body => canned_response('list_messages'), :status => 200 27 | end 28 | 29 | let(:resp) { Twilio::SMS.all } 30 | 31 | context 'using a twilio connect subaccount' do 32 | it 'uses the account sid as the username for basic auth' do 33 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 34 | to_return :body => canned_response('list_connect_messages'), :status => 200 35 | Twilio::SMS.all :account_sid => 'AC0000000000000000000000000000', :connect => true 36 | end 37 | end 38 | 39 | it 'returns a collection of objects with a length corresponding to the response' do 40 | resp.length.should == 1 41 | end 42 | 43 | it 'returns a collection containing instances of Twilio::SMS' do 44 | resp.all? { |r| r.is_a? Twilio::SMS }.should be_true 45 | end 46 | 47 | JSON.parse(canned_response('list_messages'))['sms_messages'].each_with_index do |obj,i| 48 | obj.each do |attr, value| 49 | specify { resp[i].send(attr).should == value } 50 | end 51 | end 52 | 53 | it 'accepts options to refine the search' do 54 | query = '.json?DateSent>=2010-11-12&Page=5&DateSent<=2010-12-12' 55 | stub_request(:get, resource_uri + query). 56 | to_return :body => canned_response('list_messages'), :status => 200 57 | Twilio::SMS.all :page => 5, :created_before => Date.parse('2010-12-12'), :sent_after => Date.parse('2010-11-12') 58 | a_request(:get, resource_uri + query).should have_been_made 59 | end 60 | end 61 | 62 | describe '.count' do 63 | 64 | context 'using a twilio connect subaccount' do 65 | it 'uses the account sid as the username for basic auth' do 66 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 67 | to_return :body => canned_response('list_connect_messages'), :status => 200 68 | Twilio::SMS.count :account_sid => 'AC0000000000000000000000000000', :connect => true 69 | end 70 | end 71 | 72 | it 'returns the number of resources' do 73 | stub_request(:get, resource_uri + '.json'). 74 | to_return :body => canned_response('list_messages'), :status => 200 75 | Twilio::SMS.count.should == 261 76 | end 77 | 78 | it 'accepts options to refine the search' do 79 | query = '.json?To=%2B19175551234&From=%2B19175550000' 80 | stub_request(:get, resource_uri + query). 81 | to_return :body => canned_response('list_messages'), :status => 200 82 | Twilio::SMS.count :to => '+19175551234', :from => '+19175550000' 83 | a_request(:get, resource_uri + query).should have_been_made 84 | end 85 | end 86 | 87 | describe '.find' do 88 | context 'using a twilio connect subaccount' do 89 | it 'uses the account sid as the username for basic auth' do 90 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '/SMa346467ca321c71dbd5e12f627deb854' + '.json' ). 91 | to_return :body => canned_response('connect_sms_created'), :status => 200 92 | Twilio::SMS.find 'SMa346467ca321c71dbd5e12f627deb854', :account_sid => 'AC0000000000000000000000000000', :connect => true 93 | end 94 | end 95 | 96 | context 'for a valid sms sid' do 97 | before do 98 | stub_request(:get, resource_uri + '/SM90c6fc909d8504d45ecdb3a3d5b3556e.json'). 99 | to_return :body => canned_response('sms_created'), :status => 200 100 | end 101 | 102 | let(:sms) { Twilio::SMS.find 'SM90c6fc909d8504d45ecdb3a3d5b3556e' } 103 | 104 | JSON.parse(canned_response('sms_created')).each do |k,v| 105 | specify { sms.send(k).should == v } 106 | end 107 | 108 | it 'returns an instance of Twilio::SMS' do 109 | sms.should be_a Twilio::SMS 110 | end 111 | end 112 | 113 | context 'for a string that does not correspond to a real sms' do 114 | before { stub_request(:get, resource_uri + '/phony.json').to_return :status => 404 } 115 | 116 | it 'returns nil' do 117 | sms = Twilio::SMS.find 'phony' 118 | sms.should be_nil 119 | end 120 | end 121 | end 122 | 123 | describe '.create' do 124 | context 'using a twilio connect subaccount' do 125 | it 'uses the account sid as the username for basic auth' do 126 | stub_request(:post, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 127 | with(:body => "To=%2B14155551212&From=%2B14158675309&Body=boo"). 128 | to_return :body => canned_response('connect_sms_created'), :status => 200 129 | Twilio::SMS.create :to => '+14155551212', :from => '+14158675309', :body => 'boo', :account_sid => 'AC0000000000000000000000000000', :connect => true 130 | end 131 | end 132 | context 'when authentication credentials are not configured' do 133 | it 'raises Twilio::ConfigurationError' do 134 | Twilio::Config.account_sid = nil 135 | lambda { sms }.should raise_error(Twilio::ConfigurationError) 136 | end 137 | end 138 | context 'when authentication credentials are configured' do 139 | before(:each) { stub_new_sms } 140 | 141 | it 'makes the API sms to Twilio' do 142 | sms 143 | new_sms_should_have_been_made 144 | end 145 | 146 | JSON.parse(canned_response('sms_created')).each do |k,v| 147 | specify { sms.send(k).should == v } 148 | end 149 | end 150 | end 151 | 152 | describe "#[]" do 153 | let(:sms) { Twilio::SMS.new(:to => '+19175550000') } 154 | it 'is a convenience for accessing attributes' do 155 | sms[:to].should == '+19175550000' 156 | end 157 | 158 | it 'accepts a string or symbol' do 159 | sms['to'] = '+19175559999' 160 | sms[:to].should == '+19175559999' 161 | end 162 | end 163 | 164 | describe 'behaviour on API error' do 165 | it 'raises an exception' do 166 | stub_request(:post, resource_uri + '.json').with(:body => minimum_sms_params).to_return :body => canned_response('api_error'), :status => 404 167 | lambda { sms.save }.should raise_error Twilio::APIError 168 | end 169 | end 170 | end 171 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require './lib/twilio-rb' 2 | %w. 3 | each { |lib| require lib } 4 | 5 | RSpec.configure do |config| 6 | config.after(:each) do 7 | Twilio::Config.account_sid = nil 8 | Twilio::Config.auth_token = nil 9 | WebMock.reset! 10 | end 11 | config.include WebMock::API 12 | config.mock_with 'mocha' 13 | end 14 | 15 | def canned_response(resp) 16 | File.new(File.join(File.expand_path(File.dirname __FILE__), 'support', 'responses', "#{resp}.json")).read 17 | end 18 | -------------------------------------------------------------------------------- /spec/support/responses/account.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "ACba8bc05eacf94afdae398e642c9cc32d", 3 | "friendly_name": "Do you like my friendly name?", 4 | "status": "Active", 5 | "date_created": "Wed, 04 Aug 2010 21:37:41 +0000", 6 | "date_updated": "Fri, 06 Aug 2010 01:15:02 +0000", 7 | "auth_token": "redacted", 8 | "uri": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d.json", 9 | "subresource_uris": { 10 | "available_phone_numbers": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/AvailablePhoneNumbers.json", 11 | "calls": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Calls.json", 12 | "conferences": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Conferences.json", 13 | "incoming_phone_numbers": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/IncomingPhoneNumbers.json", 14 | "notifications": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Notifications.json", 15 | "outgoing_caller_ids": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/OutgoingCallerIds.json", 16 | "recordings": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Recordings.json", 17 | "sandbox": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Sandbox.json", 18 | "sms_messages": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/SMS\/Messages.json", 19 | "transcriptions": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Transcriptions.json" 20 | } 21 | } -------------------------------------------------------------------------------- /spec/support/responses/api_error.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": 404, 3 | "message": "The requested resource was not found" 4 | } -------------------------------------------------------------------------------- /spec/support/responses/application.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "AP2a0747eba6abf96b7e3c3ff0b4530f6e", 3 | "date_created": "Mon, 16 Aug 2010 23:00:23 +0000", 4 | "date_updated": "Mon, 16 Aug 2010 23:00:23 +0000", 5 | "account_sid": "ACdc5f1e11047ebd6fe7a55f120be3a900", 6 | "friendly_name": "Phone Me", 7 | "api_version": "2010-04-01", 8 | "voice_url": "http://mycompany.com/handleNewCall.php", 9 | "voice_method": "POST", 10 | "voice_fallback_url": null, 11 | "voice_fallback_method": "POST", 12 | "status_callback": null, 13 | "status_callback_method": null, 14 | "voice_caller_id_lookup": null, 15 | "sms_url": null, 16 | "sms_method": "POST", 17 | "sms_fallback_url": null, 18 | "sms_fallback_method": "GET", 19 | "sms_status_callback": null, 20 | "uri": "\/2010-04-01\/Accounts\/ACdc5f1e11047ebd6fe7a55f120be3a900\/Applications\/AP2a0747eba6abf96b7e3c3ff0b4530f6e.json" 21 | } 22 | -------------------------------------------------------------------------------- /spec/support/responses/authorized_connect_app.json: -------------------------------------------------------------------------------- 1 | { 2 | "connect_app_sid": "CN47260e643654388faabe8aaa18ea6756", 3 | "account_sid": "AC5ef872f6da5a21de157d80997a64bd33", 4 | "permissions": ["get-all", "post-all"], 5 | "connect_app_friendly_name": "My Connect App", 6 | "connect_app_description": null, 7 | "connect_app_company_name": "My Company", 8 | "connect_app_homepage_url": "http://www.mycompany.com" 9 | } 10 | -------------------------------------------------------------------------------- /spec/support/responses/available_local_phone_numbers.json: -------------------------------------------------------------------------------- 1 | { 2 | "uri": "\/2010-04-01\/Accounts\/ACde6f1e11047ebd6fe7a55f120be3a900\/AvailablePhoneNumbers\/US\/Local.json?AreaCode=510", 3 | "available_phone_numbers": [ 4 | { 5 | "friendly_name": "(510) 564-7903", 6 | "phone_number": "+15105647903", 7 | "lata": "722", 8 | "rate_center": "OKLD TRNID", 9 | "latitude": "37.780000", 10 | "longitude": "-122.380000", 11 | "region": "CA", 12 | "postal_code": "94703", 13 | "iso_country": "US" 14 | }, 15 | { 16 | "friendly_name": "(510) 488-4379", 17 | "phone_number": "+15104884379", 18 | "lata": "722", 19 | "rate_center": "OKLD FRTVL", 20 | "latitude": "37.780000", 21 | "longitude": "-122.380000", 22 | "region": "CA", 23 | "postal_code": "94602", 24 | "iso_country": "US" 25 | } 26 | ] 27 | } 28 | 29 | -------------------------------------------------------------------------------- /spec/support/responses/available_toll_free_phone_numbers.json: -------------------------------------------------------------------------------- 1 | { 2 | "uri": "\/2010-04-01\/Accounts\/ACde6f1e11047ebd6fe7a55f120be3a900\/AvailablePhoneNumbers\/US\/TollFree.json?Contains=STORM", 3 | "available_phone_numbers": [ 4 | { 5 | "friendly_name": "(866) 557-8676", 6 | "phone_number": "+18665578676", 7 | "iso_country": "US" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /spec/support/responses/call_cancelled.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "CAa346467ca321c71dbd5e12f627deb854", 3 | "date_created": "Thu, 19 Aug 2010 00:12:15 +0000", 4 | "date_updated": "Thu, 19 Aug 2010 00:12:15 +0000", 5 | "parent_call_sid": null, 6 | "account_sid": "AC228ba7a5fe4238be081ea6f3c44186f3", 7 | "to": "+14155551212", 8 | "from": "+14158675309", 9 | "phone_number_sid": "PNd6b0e1e84f7b117332aed2fd2e5bbcab", 10 | "status": "cancelled", 11 | "start_time": null, 12 | "end_time": null, 13 | "duration": null, 14 | "price": null, 15 | "direction": "outbound-api", 16 | "answered_by": null, 17 | "api_version": "2010-04-01", 18 | "forwarded_from": null, 19 | "caller_name": null, 20 | "uri": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/Calls\/CAa346467ca321c71dbd5e12f627deb854.json", 21 | "subresource_uris": { 22 | "notifications": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/Calls\/CAa346467ca321c71dbd5e12f627deb854\/Notifications.json", 23 | "recordings": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/Calls\/CAa346467ca321c71dbd5e12f627deb854\/Recordings.json" 24 | } 25 | } -------------------------------------------------------------------------------- /spec/support/responses/call_completed.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "CAa346467ca321c71dbd5e12f627deb854", 3 | "date_created": "Thu, 19 Aug 2010 00:12:15 +0000", 4 | "date_updated": "Thu, 19 Aug 2010 00:12:15 +0000", 5 | "parent_call_sid": null, 6 | "account_sid": "AC228ba7a5fe4238be081ea6f3c44186f3", 7 | "to": "+14155551212", 8 | "from": "+14158675309", 9 | "phone_number_sid": "PNd6b0e1e84f7b117332aed2fd2e5bbcab", 10 | "status": "completed", 11 | "start_time": null, 12 | "end_time": null, 13 | "duration": null, 14 | "price": null, 15 | "direction": "outbound-api", 16 | "answered_by": null, 17 | "api_version": "2010-04-01", 18 | "forwarded_from": null, 19 | "caller_name": null, 20 | "uri": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/Calls\/CAa346467ca321c71dbd5e12f627deb854.json", 21 | "subresource_uris": { 22 | "notifications": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/Calls\/CAa346467ca321c71dbd5e12f627deb854\/Notifications.json", 23 | "recordings": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/Calls\/CAa346467ca321c71dbd5e12f627deb854\/Recordings.json" 24 | } 25 | } -------------------------------------------------------------------------------- /spec/support/responses/call_created.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "CAa346467ca321c71dbd5e12f627deb854", 3 | "date_created": "Thu, 19 Aug 2010 00:12:15 +0000", 4 | "date_updated": "Thu, 19 Aug 2010 00:12:15 +0000", 5 | "parent_call_sid": null, 6 | "account_sid": "AC228ba7a5fe4238be081ea6f3c44186f3", 7 | "to": "+14155551212", 8 | "from": "+14158675309", 9 | "phone_number_sid": "PNd6b0e1e84f7b117332aed2fd2e5bbcab", 10 | "status": "queued", 11 | "start_time": null, 12 | "end_time": null, 13 | "duration": null, 14 | "price": null, 15 | "direction": "outbound-api", 16 | "answered_by": null, 17 | "api_version": "2010-04-01", 18 | "forwarded_from": null, 19 | "caller_name": null, 20 | "fallback_method": "POST", 21 | "method": "PUT", 22 | "status_callback_method": "GET", 23 | "uri": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/Calls\/CAa346467ca321c71dbd5e12f627deb854.json", 24 | "subresource_uris": { 25 | "notifications": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/Calls\/CAa346467ca321c71dbd5e12f627deb854\/Notifications.json", 26 | "recordings": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/Calls\/CAa346467ca321c71dbd5e12f627deb854\/Recordings.json" 27 | } 28 | } -------------------------------------------------------------------------------- /spec/support/responses/call_url_modified.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "CAa346467ca321c71dbd5e12f627deb854", 3 | "date_created": "Thu, 19 Aug 2010 00:12:15 +0000", 4 | "date_updated": "Thu, 19 Aug 2010 00:12:15 +0000", 5 | "parent_call_sid": null, 6 | "account_sid": "AC228ba7a5fe4238be081ea6f3c44186f3", 7 | "to": "+14155551212", 8 | "from": "+14158675309", 9 | "phone_number_sid": "PNd6b0e1e84f7b117332aed2fd2e5bbcab", 10 | "status": "in-progress", 11 | "start_time": "Tue, 10 Aug 2010 08:02:31 +0000", 12 | "end_time": "Tue, 10 Aug 2010 08:02:47 +0000", 13 | "duration": "16", 14 | "price": "-0.03000", 15 | "direction": "outbound-api", 16 | "answered_by": null, 17 | "api_version": "2010-04-01", 18 | "annotation": null, 19 | "forwarded_from": null, 20 | "caller_name": null, 21 | "uri": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/Calls\/CAa346467ca321c71dbd5e12f627deb854.json", 22 | "subresource_uris": { 23 | "notifications": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/Calls\/CAa346467ca321c71dbd5e12f627deb854\/Notifications.json", 24 | "recordings": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/Calls\/CAa346467ca321c71dbd5e12f627deb854\/Recordings.json" 25 | } 26 | } -------------------------------------------------------------------------------- /spec/support/responses/caller_id.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "PNe905d7e6b410746a0fb08c57e5a186f3", 3 | "account_sid": "AC228ba7a5fe4238be081ea6f3c44186f3", 4 | "friendly_name": "(510) 555-5555", 5 | "phone_number": "+15105555555", 6 | "date_created": "Tue, 27 Jul 2010 20:21:11 +0000", 7 | "date_updated": "Tue, 27 Jul 2010 20:21:11 +0000", 8 | "uri": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/OutgoingCallerIds\/PNe905d7e6b410746a0fb08c57e5a186f3.json" 9 | } -------------------------------------------------------------------------------- /spec/support/responses/conference.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_sid": "AC5ef872f6da5a21de157d80997a64bd33", 3 | "api_version": "2010-04-01", 4 | "date_created": "Wed, 18 Aug 2010 20:20:06 +0000", 5 | "date_updated": "Wed, 18 Aug 2010 20:24:32 +0000", 6 | "friendly_name": "Party Line", 7 | "sid": "CFbbe46ff1274e283f7e3ac1df0072ab39", 8 | "status": "completed", 9 | "subresource_uris": { 10 | "participants": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants.json" 11 | }, 12 | "uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39.json" 13 | } -------------------------------------------------------------------------------- /spec/support/responses/connect_account.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "ACba8bc05eacf94afdae398e642c9cc32d", 3 | "friendly_name": "Do you like my friendly name?", 4 | "connect_app_sid": "CNba8bc05eacf94afdae398e642c9cc32d", 5 | "status": "Active", 6 | "date_created": "Wed, 04 Aug 2010 21:37:41 +0000", 7 | "date_updated": "Fri, 06 Aug 2010 01:15:02 +0000", 8 | "auth_token": "redacted", 9 | "uri": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d.json", 10 | "subresource_uris": { 11 | "available_phone_numbers": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/AvailablePhoneNumbers.json", 12 | "calls": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Calls.json", 13 | "conferences": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Conferences.json", 14 | "incoming_phone_numbers": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/IncomingPhoneNumbers.json", 15 | "notifications": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Notifications.json", 16 | "outgoing_caller_ids": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/OutgoingCallerIds.json", 17 | "recordings": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Recordings.json", 18 | "sandbox": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Sandbox.json", 19 | "sms_messages": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/SMS\/Messages.json", 20 | "transcriptions": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Transcriptions.json" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /spec/support/responses/connect_app.json: -------------------------------------------------------------------------------- 1 | {"sid":"CN97c262e394bc449fb1a3309fa204c4fd","account_sid":"AC228ba7a5fe4238be081ea6f3c44186f3","friendly_name":"Web Texter","description":"Example Sinatra app demonstrating how to integrate Twilio Connect and the Twilio REST API into your Ruby apps","company_name":"Twilio","homepage_url":"http:\/\/twilio.com","authorize_redirect_url":"http:\/\/127.0.0.1\/twilio\/authorize","deauthorize_callback_url":"http:\/\/127.0.0.1\/twilio\/deauthorize","deauthorize_callback_method":"POST","permissions":["get-all","post-all"],"uri":"\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/ConnectApps\/CN97c262e394bc449fb1a3309fa204c4fd.json"} 2 | -------------------------------------------------------------------------------- /spec/support/responses/connect_application.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "AP2a0747eba6abf96b7e3c3ff0b4530f6e", 3 | "connect_app_sid": "CNdc5f1e11047ebd6fe7a55f120be3a900", 4 | "date_created": "Mon, 16 Aug 2010 23:00:23 +0000", 5 | "date_updated": "Mon, 16 Aug 2010 23:00:23 +0000", 6 | "account_sid": "AC0000000000000000000000000000", 7 | "friendly_name": "Phone Me", 8 | "api_version": "2010-04-01", 9 | "voice_url": "http://mycompany.com/handleNewCall.php", 10 | "voice_method": "POST", 11 | "voice_fallback_url": null, 12 | "voice_fallback_method": "POST", 13 | "status_callback": null, 14 | "status_callback_method": null, 15 | "voice_caller_id_lookup": null, 16 | "sms_url": null, 17 | "sms_method": "POST", 18 | "sms_fallback_url": null, 19 | "sms_fallback_method": "GET", 20 | "sms_status_callback": null, 21 | "uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/Applications\/AP2a0747eba6abf96b7e3c3ff0b4530f6e.json" 22 | } 23 | -------------------------------------------------------------------------------- /spec/support/responses/connect_call_created.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "CAa346467ca321c71dbd5e12f627deb854", 3 | "date_created": "Thu, 19 Aug 2010 00:12:15 +0000", 4 | "date_updated": "Thu, 19 Aug 2010 00:12:15 +0000", 5 | "parent_call_sid": null, 6 | "account_sid": "AC0000000000000000000000000000", 7 | "connect_app_sid": "CN7c262ef394bc449fb1a3309fa204c4fd", 8 | "to": "+14155551212", 9 | "from": "+14158675309", 10 | "phone_number_sid": "PNd6b0e1e84f7b117332aed2fd2e5bbcab", 11 | "status": "queued", 12 | "start_time": null, 13 | "end_time": null, 14 | "duration": null, 15 | "price": null, 16 | "direction": "outbound-api", 17 | "answered_by": null, 18 | "api_version": "2010-04-01", 19 | "forwarded_from": null, 20 | "caller_name": null, 21 | "fallback_method": "POST", 22 | "method": "PUT", 23 | "status_callback_method": "GET", 24 | "uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/Calls\/CAa346467ca321c71dbd5e12f627deb854.json", 25 | "subresource_uris": { 26 | "notifications": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/Calls\/CAa346467ca321c71dbd5e12f627deb854\/Notifications.json", 27 | "recordings": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/Calls\/CAa346467ca321c71dbd5e12f627deb854\/Recordings.json" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /spec/support/responses/connect_caller_id.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "PNe905d7e6b410746a0fb08c57e5a186f3", 3 | "account_sid": "AC0000000000000000000000000000", 4 | "connect_app_sid": "CN228ba7a5fe4238be081ea6f3c44186f3", 5 | "friendly_name": "(510) 555-5555", 6 | "phone_number": "+15105555555", 7 | "date_created": "Tue, 27 Jul 2010 20:21:11 +0000", 8 | "date_updated": "Tue, 27 Jul 2010 20:21:11 +0000", 9 | "uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/OutgoingCallerIds\/PNe905d7e6b410746a0fb08c57e5a186f3.json" 10 | } 11 | -------------------------------------------------------------------------------- /spec/support/responses/connect_conference.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_sid": "AC0000000000000000000000000000", 3 | "api_version": "2010-04-01", 4 | "connect_app_sid": "5ef872f6da5a21de157d80997a64bd33", 5 | "date_created": "Wed, 18 Aug 2010 20:20:06 +0000", 6 | "date_updated": "Wed, 18 Aug 2010 20:24:32 +0000", 7 | "friendly_name": "Party Line", 8 | "sid": "CFbbe46ff1274e283f7e3ac1df0072ab39", 9 | "status": "completed", 10 | "subresource_uris": { 11 | "participants": "/2010-04-01/Accounts/AC0000000000000000000000000000/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants.json" 12 | }, 13 | "uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39.json" 14 | } 15 | -------------------------------------------------------------------------------- /spec/support/responses/connect_incoming_phone_number.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "PN2a0747eba6abf96b7e3c3ff0b4530f6e", 3 | "account_sid": "AC0000000000000000000000000000", 4 | "connect_app_sid": "CNdc5f1e11047ebd6fe7a55f120be3a900", 5 | "friendly_name": "My Company Line", 6 | "phone_number": "+15105647903", 7 | "voice_url": "http://mycompany.com/handleNewCall.php", 8 | "voice_method": "POST", 9 | "voice_fallback_url": null, 10 | "voice_fallback_method": "POST", 11 | "voice_caller_idlookup": null, 12 | "date_created": "Mon, 16 Aug 2010 23:00:23 +0000", 13 | "date_updated": "Mon, 16 Aug 2010 23:00:23 +0000", 14 | "sms_url": null, 15 | "sms_method": "POST", 16 | "sms_fallback_url": null, 17 | "sms_fallback_method": "GET", 18 | "capabilities": { 19 | "voice": null, 20 | "sms": null 21 | }, 22 | "status_callback": null, 23 | "status_callback_method": null, 24 | "api_version": "2010-04-01", 25 | "uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/IncomingPhoneNumbers\/PN2a0747eba6abf96b7e3c3ff0b4530f6e.json" 26 | } 27 | 28 | -------------------------------------------------------------------------------- /spec/support/responses/connect_notification.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "NO5a7a84730f529f0a76b3e30c01315d1a", 3 | "account_sid": "AC0000000000000000000000000000", 4 | "connect_app_sid": "CNda6f1e11047ebd6fe7a55f120be3a900", 5 | "call_sid": "CAa8857b0dcc71b4909aced594f7f87453", 6 | "log": "0", 7 | "error_code": "11205", 8 | "more_info": "http:\/\/www.twilio.com\/docs\/errors\/11205", 9 | "message_text": "EmailNotification=false&LogLevel=ERROR&sourceComponent=13400&Msg=HTTP+Connection+Failure+-+Read+timed+out&ErrorCode=11205&msg=HTTP+Connection+Failure+-+Read+timed+out&url=4min19secs.mp3", 10 | "message_date": "Tue, 09 Feb 2010 01:23:53 +0000", 11 | "response_body": "\n\n\t4min19secs.mp3<\/Play>\n<\/Response>\n", 12 | "request_method": "GET", 13 | "request_url": "http:\/\/demo.twilio.com\/welcome", 14 | "request_variables": "AccountSid=AC0000000000000000000000000000&CallStatus=in-progress&Called=4152374451&CallerCountry=US&CalledZip=94937&CallerCity=&Caller=4150000000&CalledCity=INVERNESS&CalledCountry=US&DialStatus=answered&CallerState=California&CallSid=CAa8857b0dcc71b4909aced594f7f87453&CalledState=CA&CallerZip=", 15 | "response_headers": "Date=Tue%2C+09+Feb+2010+01%3A23%3A38+GMT&Vary=Accept-Encoding&Content-Length=91&Content-Type=text%2Fxml&Accept-Ranges=bytes&Server=Apache%2F2.2.3+%28CentOS%29&X-Powered-By=PHP%2F5.1.6", 16 | "date_created": "Tue, 09 Feb 2010 01:23:53 +0000", 17 | "api_version": "2008-08-01", 18 | "date_updated": "Tue, 09 Feb 2010 01:23:53 +0000", 19 | "uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/Notifications\/NO5a7a84730f529f0a76b3e30c01315d1a.json" 20 | } 21 | -------------------------------------------------------------------------------- /spec/support/responses/connect_recording.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_sid": "AC0000000000000000000000000000", 3 | "connect_app_sid": "CNda6f1e11047ebd6fe7a55f120be3a900", 4 | "api_version": "2008-08-01", 5 | "call_sid": "CA8dfedb55c129dd4d6bd1f59af9d11080", 6 | "date_created": "Fri, 17 Jul 2009 01:52:49 +0000", 7 | "date_updated": "Fri, 17 Jul 2009 01:52:49 +0000", 8 | "duration": "1", 9 | "sid": "RE557ce644e5ab84fa21cc21112e22c485", 10 | "uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Recordings/RE557ce644e5ab84fa21cc21112e22c485.json" 11 | } 12 | -------------------------------------------------------------------------------- /spec/support/responses/connect_sms_created.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_sid": "AC0000000000000000000000000000", 3 | "connect_app_sid": "CN0000000000000000000000000000", 4 | "api_version": "2010-04-01", 5 | "body": "Jenny please?! I love you <3", 6 | "date_created": "Wed, 18 Aug 2010 20:01:40 +0000", 7 | "date_sent": null, 8 | "date_updated": "Wed, 18 Aug 2010 20:01:40 +0000", 9 | "direction": "outbound-api", 10 | "from": "+14158141829", 11 | "price": null, 12 | "sid": "SM90c6fc909d8504d45ecdb3a3d5b3556e", 13 | "status": "queued", 14 | "to": "+14159352345", 15 | "uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/SMS/Messages/SM90c6fc909d8504d45ecdb3a3d5b3556e.json" 16 | } 17 | -------------------------------------------------------------------------------- /spec/support/responses/connect_transcription.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_sid": "AC0000000000000000000000000000", 3 | "api_version": "2008-08-01", 4 | "connect_app_sid": "CN5ef872f6da5a21de157d80997a64bd33", 5 | "date_created": "Mon, 26 Jul 2010 00:09:58 +0000", 6 | "date_updated": "Mon, 26 Jul 2010 00:10:25 +0000", 7 | "duration": "6", 8 | "price": "-0.05000", 9 | "recording_sid": "REca11f06dc31b5515a2dfb1f5134361f2", 10 | "sid": "TR8c61027b709ffb038236612dc5af8723", 11 | "status": "completed", 12 | "transcription_text": "Tommy? Tommy is that you? I told you never to call me again.", 13 | "type": "fast", 14 | "uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Transcriptions/TR8c61027b709ffb038236612dc5af8723.json" 15 | } 16 | -------------------------------------------------------------------------------- /spec/support/responses/incoming_phone_number.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "PN2a0747eba6abf96b7e3c3ff0b4530f6e", 3 | "account_sid": "ACdc5f1e11047ebd6fe7a55f120be3a900", 4 | "friendly_name": "My Company Line", 5 | "phone_number": "+15105647903", 6 | "voice_url": "http://mycompany.com/handleNewCall.php", 7 | "voice_method": "POST", 8 | "voice_fallback_url": null, 9 | "voice_fallback_method": "POST", 10 | "voice_caller_idlookup": null, 11 | "date_created": "Mon, 16 Aug 2010 23:00:23 +0000", 12 | "date_updated": "Mon, 16 Aug 2010 23:00:23 +0000", 13 | "sms_url": null, 14 | "sms_method": "POST", 15 | "sms_fallback_url": null, 16 | "sms_fallback_method": "GET", 17 | "capabilities": { 18 | "voice": null, 19 | "sms": null 20 | }, 21 | "status_callback": null, 22 | "status_callback_method": null, 23 | "api_version": "2010-04-01", 24 | "uri": "\/2010-04-01\/Accounts\/ACdc5f1e11047ebd6fe7a55f120be3a900\/IncomingPhoneNumbers\/PN2a0747eba6abf96b7e3c3ff0b4530f6e.json" 25 | } 26 | 27 | -------------------------------------------------------------------------------- /spec/support/responses/list_accounts.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 0, 3 | "num_pages": 1, 4 | "page_size": 50, 5 | "total": 6, 6 | "start": 0, 7 | "end": 5, 8 | "uri": "\/2010-04-01\/Accounts.json", 9 | "first_page_uri": "\/2010-04-01\/Accounts.json?Page=0&PageSize=50", 10 | "previous_page_uri": null, 11 | "next_page_uri": null, 12 | "last_page_uri": "\/2010-04-01\/Accounts.json?Page=0&PageSize=50", 13 | "accounts": [ 14 | { 15 | "sid": "ACba8bc05eacf94afdae398e642c9cc32d", 16 | "friendly_name": "Do you like my friendly name?", 17 | "status": "Active", 18 | "date_created": "Wed, 04 Aug 2010 21:37:41 +0000", 19 | "date_updated": "Fri, 06 Aug 2010 01:15:02 +0000", 20 | "auth_token": "redacted", 21 | "uri": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d.json", 22 | "subresource_uris": { 23 | "available_phone_numbers": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/AvailablePhoneNumbers.json", 24 | "calls": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Calls.json", 25 | "conferences": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Conferences.json", 26 | "incoming_phone_numbers": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/IncomingPhoneNumbers.json", 27 | "notifications": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Notifications.json", 28 | "outgoing_caller_ids": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/OutgoingCallerIds.json", 29 | "recordings": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Recordings.json", 30 | "sandbox": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Sandbox.json", 31 | "sms_messages": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/SMS\/Messages.json", 32 | "transcriptions": "\/2010-04-01\/Accounts\/ACba8bc05eacf94afdae398e642c9cc32d\/Transcriptions.json" 33 | } 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /spec/support/responses/list_applications.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 0, 3 | "num_pages": 1, 4 | "page_size": 50, 5 | "total": 6, 6 | "start": 0, 7 | "end": 5, 8 | "uri": "\/2010-04-01\/Accounts\/ACdc5f1e11047ebd6fe7a55f120be3a900\/Applications.json", 9 | "first_page_uri": "\/2010-04-01\/Accounts\/ACdc5f1e11047ebd6fe7a55f120be3a900\/Applications.json?Page=0&PageSize=50", 10 | "previous_page_uri": null, 11 | "next_page_uri": null, 12 | "last_page_uri": "\/2010-04-01\/Accounts\/ACdc5f1e11047ebd6fe7a55f120be3a900\/Applications.json?Page=0&PageSize=50", 13 | "applications": [ 14 | { 15 | "sid": "AP3f94c94562ac88dccf16f8859a1a8b25", 16 | "date_created": "Thu, 13 Nov 2008 07:56:24 +0000", 17 | "date_updated": "Thu, 13 Nov 2008 08:45:58 +0000", 18 | "account_sid": "ACdc5f1e11047ebd6fe7a55f120be3a900", 19 | "friendly_name": "Long Play", 20 | "api_version": "2010-04-01", 21 | "voice_url": "http:\/\/demo.twilio.com\/long", 22 | "voice_method": "GET", 23 | "voice_fallback_url": null, 24 | "voice_fallback_method": null, 25 | "status_callback": null, 26 | "status_callback_method": null, 27 | "voice_caller_id_lookup": null, 28 | "sms_url": null, 29 | "sms_method": null, 30 | "sms_fallback_url": null, 31 | "sms_fallback_method": null, 32 | "sms_status_callback": null, 33 | "uri": "\/2010-04-01\/Accounts\/ACdc5f1e11047ebd6fe7a55f120be3a900\/Applications\/AP3f94c94562ac88dccf16f8859a1a8b25.json" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /spec/support/responses/list_authorized_connect_apps.json: -------------------------------------------------------------------------------- 1 | { 2 | "authorized_connect_apps": [ 3 | { 4 | "connect_app_sid": "CNb989fdd207b04d16aee578018ef5fd93", 5 | "account_sid": "AC828fd6e37bee469a9aee33475d67db2c", 6 | "permissions": ["get-all", "post-all"], 7 | "connect_app_friendly_name": "Jenny Tracker", 8 | "connect_app_description": null, 9 | "connect_app_company_name": "Tommy PI", 10 | "connect_app_homepage_url": "http://www.tommypi.com" 11 | } 12 | ], 13 | "page": 0, 14 | "num_pages": 1, 15 | "page_size": 50, 16 | "total": 3, 17 | "start": 0, 18 | "end": 2, 19 | "uri": "\/2010-04-01\/Accounts\/AC81e0ca4b0af06b833b6726957613c5d4\/AuthorizedConnectApps\/.json", 20 | "first_page_uri": "\/2010-04-01\/Accounts\/AC81e0ca4b0af06b833b6726957613c5d4\/AuthorizedConnectApps\/.json?Page=0&PageSize=50", 21 | "previous_page_uri": null, 22 | "next_page_uri": null, 23 | "last_page_uri": "\/2010-04-01\/Accounts\/AC81e0ca4b0af06b833b6726957613c5d4\/AuthorizedConnectApps\/.json?Page=0&PageSize=50" 24 | } 25 | 26 | -------------------------------------------------------------------------------- /spec/support/responses/list_caller_ids.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 0, 3 | "num_pages": 1, 4 | "page_size": 50, 5 | "total": 1, 6 | "start": 0, 7 | "end": 0, 8 | "uri": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/OutgoingCallerIds.json", 9 | "first_page_uri": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/OutgoingCallerIds.json?Page=0&PageSize=50", 10 | "previous_page_uri": null, 11 | "next_page_uri": null, 12 | "last_page_uri": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/OutgoingCallerIds.json?Page=0&PageSize=50", 13 | "outgoing_caller_ids": [ 14 | { 15 | "sid": "PNe905d7e6b410746a0fb08c57e5a186f3", 16 | "account_sid": "AC228ba7a5fe4238be081ea6f3c44186f3", 17 | "friendly_name": "(510) 555-5555", 18 | "phone_number": "+15105555555", 19 | "date_created": "Tue, 27 Jul 2010 20:21:11 +0000", 20 | "date_updated": "Tue, 27 Jul 2010 20:21:11 +0000", 21 | "uri": "\/2010-04-01\/Accounts\/AC228ba7a5fe4238be081ea6f3c44186f3\/OutgoingCallerIds\/PNe905d7e6b410746a0fb08c57e5a186f3.json" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /spec/support/responses/list_calls.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 0, 3 | "num_pages": 3, 4 | "page_size": 50, 5 | "total": 147, 6 | "start": 0, 7 | "end": 49, 8 | "uri": "\/2010-04-01\/Accounts\/AC5ef877a5fe4238be081ea6f3c44186f3\/Calls.json", 9 | "first_page_uri": "\/2010-04-01\/Accounts\/AC5ef877a5fe4238be081ea6f3c44186f3\/Calls.json?Page=0&PageSize=50", 10 | "previous_page_uri": null, 11 | "next_page_uri": "\/2010-04-01\/Accounts\/AC5ef877a5fe4238be081ea6f3c44186f3\/Calls.json?Page=1&PageSize=50", 12 | "last_page_uri": "\/2010-04-01\/Accounts\/AC5ef877a5fe4238be081ea6f3c44186f3\/Calls.json?Page=2&PageSize=50", 13 | "calls": [ 14 | { 15 | "sid": "CA92d4405c9237c4ea04b56cbda88e128c", 16 | "date_created": "Fri, 13 Aug 2010 01:16:22 +0000", 17 | "date_updated": "Fri, 13 Aug 2010 01:16:22 +0000", 18 | "parent_call_sid": null, 19 | "account_sid": "AC5ef877a5fe4238be081ea6f3c44186f3", 20 | "to": "+15304551166", 21 | "from": "+15105555555", 22 | "phone_number_sid": "PNe2d8e63b37f46f2adb16f228afdb9058", 23 | "status": "queued", 24 | "start_time": null, 25 | "end_time": null, 26 | "duration": null, 27 | "price": null, 28 | "direction": "outbound-api", 29 | "answered_by": null, 30 | "api_version": "2010-04-01", 31 | "forwarded_from": null, 32 | "caller_name": null, 33 | "uri": "\/2010-04-01\/Accounts\/AC5ef877a5fe4238be081ea6f3c44186f3\/Calls\/CA92d4405c9237c4ea04b56cbda88e128c.json", 34 | "subresource_uris": { 35 | "notifications": "\/2010-04-01\/Accounts\/AC5ef877a5fe4238be081ea6f3c44186f3\/Calls\/CA92d4405c9237c4ea04b56cbda88e128c\/Notifications.json", 36 | "recordings": "\/2010-04-01\/Accounts\/AC5ef877a5fe4238be081ea6f3c44186f3\/Calls\/CA92d4405c9237c4ea04b56cbda88e128c\/Recordings.json" 37 | } 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /spec/support/responses/list_conferences.json: -------------------------------------------------------------------------------- 1 | { 2 | "end": 49, 3 | "first_page_uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Conferences.json?Page=0&PageSize=50", 4 | "last_page_uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Conferences.json?Page=9&PageSize=50", 5 | "next_page_uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Conferences.json?Page=1&PageSize=50", 6 | "num_pages": 10, 7 | "page": 0, 8 | "page_size": 50, 9 | "previous_page_uri": null, 10 | "start": 0, 11 | "total": 462, 12 | "uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Conferences.json", 13 | "conferences": [ 14 | { 15 | "account_sid": "AC5ef872f6da5a21de157d80997a64bd33", 16 | "api_version": "2010-04-01", 17 | "date_created": "Wed, 18 Aug 2010 20:20:06 +0000", 18 | "date_updated": "Wed, 18 Aug 2010 20:24:32 +0000", 19 | "friendly_name": "Party Line", 20 | "sid": "CFbbe46ff1274e283f7e3ac1df0072ab39", 21 | "status": "completed", 22 | "subresource_uris": { 23 | "participants": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants.json" 24 | }, 25 | "uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39.json" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /spec/support/responses/list_connect_applications.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 0, 3 | "num_pages": 1, 4 | "page_size": 50, 5 | "total": 6, 6 | "start": 0, 7 | "end": 5, 8 | "uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/Applications.json", 9 | "first_page_uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/Applications.json?Page=0&PageSize=50", 10 | "previous_page_uri": null, 11 | "next_page_uri": null, 12 | "last_page_uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/Applications.json?Page=0&PageSize=50", 13 | "applications": [ 14 | { 15 | "sid": "AP3f94c94562ac88dccf16f8859a1a8b25", 16 | "connect_app_sid": "CNdc5f1e11047ebd6fe7a55f120be3a900", 17 | "date_created": "Thu, 13 Nov 2008 07:56:24 +0000", 18 | "date_updated": "Thu, 13 Nov 2008 08:45:58 +0000", 19 | "account_sid": "AC0000000000000000000000000000", 20 | "friendly_name": "Long Play", 21 | "api_version": "2010-04-01", 22 | "voice_url": "http:\/\/demo.twilio.com\/long", 23 | "voice_method": "GET", 24 | "voice_fallback_url": null, 25 | "voice_fallback_method": null, 26 | "status_callback": null, 27 | "status_callback_method": null, 28 | "voice_caller_id_lookup": null, 29 | "sms_url": null, 30 | "sms_method": null, 31 | "sms_fallback_url": null, 32 | "sms_fallback_method": null, 33 | "sms_status_callback": null, 34 | "uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/Applications\/AP3f94c94562ac88dccf16f8859a1a8b25.json" 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /spec/support/responses/list_connect_apps.json: -------------------------------------------------------------------------------- 1 | { 2 | "connect_apps": [ 3 | { 4 | "sid": "CNb989fdd207b04d16aee578018ef5fd93", 5 | "account_sid": "AC72d87bea57556989c033c644f6270b8e", 6 | "friendly_name": "My awesome ConnectApp", 7 | "description": "An amazing TwilioConnect application that does amazing things!", 8 | "company_name": "My Company", 9 | "homepage_url": "http://www.mycompany.com", 10 | "authorize_redirect_url": "https://www.mycompany.com/connect_authorize", 11 | "deauthorize_callback_url": "https://www.mycompany.com/connect_deauthorize", 12 | "deauthorize_callback_method": "POST", 13 | "permissions": ["get-all","post-all"] 14 | } 15 | ], 16 | "page": 0, 17 | "num_pages": 1, 18 | "page_size": 50, 19 | "total": 3, 20 | "start": 0, 21 | "end": 2, 22 | "uri": "\/2010-04-01\/Accounts\/AC81e0ca4b0af06b833b6726957613c5d4\/ConnectApps\/.json", 23 | "first_page_uri": "\/2010-04-01\/Accounts\/AC81e0ca4b0af06b833b6726957613c5d4\/ConnectApps\/.json?Page=0&PageSize=50", 24 | "previous_page_uri": null, 25 | "next_page_uri": null, 26 | "last_page_uri": "\/2010-04-01\/Accounts\/AC81e0ca4b0af06b833b6726957613c5d4\/ConnectApps\/.json?Page=0&PageSize=50" 27 | } 28 | -------------------------------------------------------------------------------- /spec/support/responses/list_connect_caller_ids.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 0, 3 | "num_pages": 1, 4 | "page_size": 50, 5 | "total": 1, 6 | "start": 0, 7 | "end": 0, 8 | "uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/OutgoingCallerIds.json", 9 | "first_page_uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/OutgoingCallerIds.json?Page=0&PageSize=50", 10 | "previous_page_uri": null, 11 | "next_page_uri": null, 12 | "last_page_uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/OutgoingCallerIds.json?Page=0&PageSize=50", 13 | "outgoing_caller_ids": [ 14 | { 15 | "sid": "PNe905d7e6b410746a0fb08c57e5a186f3", 16 | "account_sid": "AC0000000000000000000000000000", 17 | "connect_app_sid": "CN228ba7a5fe4238be081ea6f3c44186f3", 18 | "friendly_name": "(510) 555-5555", 19 | "phone_number": "+15105555555", 20 | "date_created": "Tue, 27 Jul 2010 20:21:11 +0000", 21 | "date_updated": "Tue, 27 Jul 2010 20:21:11 +0000", 22 | "uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/OutgoingCallerIds\/PNe905d7e6b410746a0fb08c57e5a186f3.json" 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /spec/support/responses/list_connect_calls.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 0, 3 | "num_pages": 3, 4 | "page_size": 50, 5 | "total": 147, 6 | "start": 0, 7 | "end": 49, 8 | "uri": "\/2010-04-01\/Accounts\/AC5ef877a5fe4238be081ea6f3c44186f3\/Calls.json", 9 | "first_page_uri": "\/2010-04-01\/Accounts\/AC5ef877a5fe4238be081ea6f3c44186f3\/Calls.json?Page=0&PageSize=50", 10 | "previous_page_uri": null, 11 | "next_page_uri": "\/2010-04-01\/Accounts\/AC5ef877a5fe4238be081ea6f3c44186f3\/Calls.json?Page=1&PageSize=50", 12 | "last_page_uri": "\/2010-04-01\/Accounts\/AC5ef877a5fe4238be081ea6f3c44186f3\/Calls.json?Page=2&PageSize=50", 13 | "calls": [ 14 | { 15 | "sid": "CA92d4405c9237c4ea04b56cbda88e128c", 16 | "date_created": "Fri, 13 Aug 2010 01:16:22 +0000", 17 | "date_updated": "Fri, 13 Aug 2010 01:16:22 +0000", 18 | "connect_app_sid": "CN7c262ef394bc449fb1a3309fa204c4fd", 19 | "parent_call_sid": null, 20 | "account_sid": "AC5ef877a5fe4238be081ea6f3c44186f3", 21 | "to": "+15304551166", 22 | "from": "+15105555555", 23 | "phone_number_sid": "PNe2d8e63b37f46f2adb16f228afdb9058", 24 | "status": "queued", 25 | "start_time": null, 26 | "end_time": null, 27 | "duration": null, 28 | "price": null, 29 | "direction": "outbound-api", 30 | "answered_by": null, 31 | "api_version": "2010-04-01", 32 | "forwarded_from": null, 33 | "caller_name": null, 34 | "uri": "\/2010-04-01\/Accounts\/AC5ef877a5fe4238be081ea6f3c44186f3\/Calls\/CA92d4405c9237c4ea04b56cbda88e128c.json", 35 | "subresource_uris": { 36 | "notifications": "\/2010-04-01\/Accounts\/AC5ef877a5fe4238be081ea6f3c44186f3\/Calls\/CA92d4405c9237c4ea04b56cbda88e128c\/Notifications.json", 37 | "recordings": "\/2010-04-01\/Accounts\/AC5ef877a5fe4238be081ea6f3c44186f3\/Calls\/CA92d4405c9237c4ea04b56cbda88e128c\/Recordings.json" 38 | } 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /spec/support/responses/list_connect_conferences.json: -------------------------------------------------------------------------------- 1 | { 2 | "end": 49, 3 | "first_page_uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Conferences.json?Page=0&PageSize=50", 4 | "last_page_uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Conferences.json?Page=9&PageSize=50", 5 | "next_page_uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Conferences.json?Page=1&PageSize=50", 6 | "num_pages": 10, 7 | "page": 0, 8 | "page_size": 50, 9 | "previous_page_uri": null, 10 | "start": 0, 11 | "total": 462, 12 | "uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Conferences.json", 13 | "conferences": [ 14 | { 15 | "account_sid": "AC0000000000000000000000000000", 16 | "api_version": "2010-04-01", 17 | "connect_app_sid": "CNbbe46ff1274e283f7e3ac1df0072ab39", 18 | "date_created": "Wed, 18 Aug 2010 20:20:06 +0000", 19 | "date_updated": "Wed, 18 Aug 2010 20:24:32 +0000", 20 | "friendly_name": "Party Line", 21 | "sid": "CFbbe46ff1274e283f7e3ac1df0072ab39", 22 | "status": "completed", 23 | "subresource_uris": { 24 | "participants": "/2010-04-01/Accounts/AC0000000000000000000000000000/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants.json" 25 | }, 26 | "uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39.json" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /spec/support/responses/list_connect_incoming_phone_numbers.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 0, 3 | "num_pages": 1, 4 | "page_size": 50, 5 | "total": 6, 6 | "start": 0, 7 | "end": 5, 8 | "uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/IncomingPhoneNumbers.json", 9 | "first_page_uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/IncomingPhoneNumbers.json?Page=0&PageSize=50", 10 | "previous_page_uri": null, 11 | "next_page_uri": null, 12 | "last_page_uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/IncomingPhoneNumbers.json?Page=0&PageSize=50", 13 | "incoming_phone_numbers": [ 14 | { 15 | "sid": "PN3f94c94562ac88dccf16f8859a1a8b25", 16 | "connect_app_sid": "CNdc5f1e11047ebd6fe7a55f120be3a900", 17 | "account_sid": "AC0000000000000000000000000000", 18 | "friendly_name": "Long Play", 19 | "phone_number": "+14152374451", 20 | "voice_url": "http:\/\/demo.twilio.com\/long", 21 | "voice_method": "GET", 22 | "voice_fallback_url": null, 23 | "voice_fallback_method": null, 24 | "voice_caller_idlookup": null, 25 | "date_created": "Thu, 13 Nov 2008 07:56:24 +0000", 26 | "date_updated": "Thu, 13 Nov 2008 08:45:58 +0000", 27 | "sms_url": null, 28 | "sms_method": null, 29 | "sms_fallback_url": null, 30 | "sms_fallback_method": null, 31 | "capabilities": { 32 | "voice": true, 33 | "sms": false 34 | }, 35 | "status_callback": null, 36 | "status_callback_method": null, 37 | "api_version": "2010-04-01", 38 | "uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/IncomingPhoneNumbers\/PN3f94c94562ac88dccf16f8859a1a8b25.json" 39 | } 40 | ] 41 | } 42 | 43 | -------------------------------------------------------------------------------- /spec/support/responses/list_connect_messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "start": 0, 3 | "total": 261, 4 | "num_pages": 6, 5 | "page": 0, 6 | "page_size": 50, 7 | "end": 49, 8 | "uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/SMS/Messages.json", 9 | "first_page_uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/SMS/Messages.json?Page=0&PageSize=50", 10 | "last_page_uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/SMS/Messages.json?Page=5&PageSize=50", 11 | "next_page_uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/SMS/Messages.json?Page=1&PageSize=50", 12 | "previous_page_uri": null, 13 | "sms_messages": [ 14 | { 15 | "account_sid": "AC0000000000000000000000000000", 16 | "api_version": "2008-08-01", 17 | "connect_app_sid": "CN5ef872f6da5a21de157d80997a64bd33", 18 | "body": "Hey Jenny why aren't you returning my calls?", 19 | "date_created": "Mon, 16 Aug 2010 03:45:01 +0000", 20 | "date_sent": "Mon, 16 Aug 2010 03:45:03 +0000", 21 | "date_updated": "Mon, 16 Aug 2010 03:45:03 +0000", 22 | "direction": "outbound-api", 23 | "from": "+14158141829", 24 | "price": "-0.02000", 25 | "sid": "SM800f449d0399ed014aae2bcc0cc2f2ec", 26 | "status": "sent", 27 | "to": "+14159978453", 28 | "uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/SMS/Messages/SM800f449d0399ed014aae2bcc0cc2f2ec.json" 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /spec/support/responses/list_connect_notifications.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 0, 3 | "num_pages": 25, 4 | "page_size": 50, 5 | "total": 1224, 6 | "start": 0, 7 | "end": 49, 8 | "uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/Notifications.json", 9 | "first_page_uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/Notifications.json?Page=0&PageSize=50", 10 | "previous_page_uri": null, 11 | "next_page_uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/Notifications.json?Page=1&PageSize=50", 12 | "last_page_uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/Notifications.json?Page=24&PageSize=50", 13 | "notifications": [ 14 | { 15 | "sid": "NO5a7a84730f529f0a76b3e30c01315d1a", 16 | "account_sid": "AC0000000000000000000000000000", 17 | "connect_app_sid": "CNda6f1e11047ebd6fe7a55f120be3a900", 18 | "call_sid": "CAa8857b0dcc71b4909aced594f7f87453", 19 | "log": "0", 20 | "error_code": "11205", 21 | "more_info": "http:\/\/www.twilio.com\/docs\/errors\/11205", 22 | "message_text": "EmailNotification=false&LogLevel=ERROR&sourceComponent=13400&Msg=HTTP+Connection+Failure+-+Read+timed+out&ErrorCode=11205&msg=HTTP+Connection+Failure+-+Read+timed+out&url=4min19secs.mp3", 23 | "message_date": "Tue, 09 Feb 2010 01:23:53 +0000", 24 | "request_method": "POST", 25 | "request_url": "http:\/\/demo.twilio.com\/welcome", 26 | "date_created": "Tue, 09 Feb 2010 01:23:53 +0000", 27 | "api_version": "2008-08-01", 28 | "date_updated": "Tue, 09 Feb 2010 01:23:53 +0000", 29 | "uri": "\/2010-04-01\/Accounts\/AC0000000000000000000000000000\/Notifications\/NO5a7a84730f529f0a76b3e30c01315d1a.json" 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /spec/support/responses/list_connect_recordings.json: -------------------------------------------------------------------------------- 1 | { 2 | "start": 0, 3 | "end": 49, 4 | "total": 527, 5 | "num_pages": 11, 6 | "page": 0, 7 | "page_size": 50, 8 | "uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Recordings.json", 9 | "first_page_uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Recordings.json?Page=0&PageSize=50", 10 | "last_page_uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Recordings.json?Page=10&PageSize=50", 11 | "previous_page_uri": null, 12 | "next_page_uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Recordings.json?Page=1&PageSize=50", 13 | "recordings": [ 14 | { 15 | "account_sid": "AC0000000000000000000000000000", 16 | "connect_app_sid": "CNda6f1e11047ebd6fe7a55f120be3a900", 17 | "api_version": "2008-08-01", 18 | "call_sid": "CA8dfedb55c129dd4d6bd1f59af9d11080", 19 | "date_created": "Fri, 17 Jul 2009 01:52:49 +0000", 20 | "date_updated": "Fri, 17 Jul 2009 01:52:49 +0000", 21 | "duration": "1", 22 | "sid": "RE557ce644e5ab84fa21cc21112e22c485", 23 | "uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Recordings/RE557ce644e5ab84fa21cc21112e22c485.json" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /spec/support/responses/list_connect_transcriptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "end": 49, 3 | "num_pages": 3, 4 | "page": 0, 5 | "page_size": 50, 6 | "previous_page_uri": null, 7 | "start": 0, 8 | "total": 150, 9 | "first_page_uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Transcriptions.json?Page=0&PageSize=50", 10 | "last_page_uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Transcriptions.json?Page=2&PageSize=50", 11 | "next_page_uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Transcriptions.json?Page=1&PageSize=50", 12 | "uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Transcriptions.json", 13 | "transcriptions": [ 14 | { 15 | "account_sid": "AC0000000000000000000000000000", 16 | "api_version": "2010-04-01", 17 | "connect_app_sid": "CN5ef872f6da5a21de157d80997a64bd33", 18 | "date_created": "Mon, 26 Jul 2010 00:09:58 +0000", 19 | "date_updated": "Mon, 26 Jul 2010 00:10:25 +0000", 20 | "duration": "6", 21 | "price": "-0.05000", 22 | "recording_sid": "REca11f06dc31b5515a2dfb1f5134361f2", 23 | "sid": "TR8c61027b709ffb038236612dc5af8723", 24 | "status": "completed", 25 | "transcription_text": "Tommy? Tommy is that you? I told you never to call me again.", 26 | "type": "fast", 27 | "uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Transcriptions/TR8c61027b709ffb038236612dc5af8723.json" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /spec/support/responses/list_incoming_phone_numbers.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 0, 3 | "num_pages": 1, 4 | "page_size": 50, 5 | "total": 6, 6 | "start": 0, 7 | "end": 5, 8 | "uri": "\/2010-04-01\/Accounts\/ACdc5f1e11047ebd6fe7a55f120be3a900\/IncomingPhoneNumbers.json", 9 | "first_page_uri": "\/2010-04-01\/Accounts\/ACdc5f1e11047ebd6fe7a55f120be3a900\/IncomingPhoneNumbers.json?Page=0&PageSize=50", 10 | "previous_page_uri": null, 11 | "next_page_uri": null, 12 | "last_page_uri": "\/2010-04-01\/Accounts\/ACdc5f1e11047ebd6fe7a55f120be3a900\/IncomingPhoneNumbers.json?Page=0&PageSize=50", 13 | "incoming_phone_numbers": [ 14 | { 15 | "sid": "PN3f94c94562ac88dccf16f8859a1a8b25", 16 | "account_sid": "ACdc5f1e11047ebd6fe7a55f120be3a900", 17 | "friendly_name": "Long Play", 18 | "phone_number": "+14152374451", 19 | "voice_url": "http:\/\/demo.twilio.com\/long", 20 | "voice_method": "GET", 21 | "voice_fallback_url": null, 22 | "voice_fallback_method": null, 23 | "voice_caller_idlookup": null, 24 | "date_created": "Thu, 13 Nov 2008 07:56:24 +0000", 25 | "date_updated": "Thu, 13 Nov 2008 08:45:58 +0000", 26 | "sms_url": null, 27 | "sms_method": null, 28 | "sms_fallback_url": null, 29 | "sms_fallback_method": null, 30 | "capabilities": { 31 | "voice": true, 32 | "sms": false 33 | }, 34 | "status_callback": null, 35 | "status_callback_method": null, 36 | "api_version": "2010-04-01", 37 | "uri": "\/2010-04-01\/Accounts\/ACdc5f1e11047ebd6fe7a55f120be3a900\/IncomingPhoneNumbers\/PN3f94c94562ac88dccf16f8859a1a8b25.json" 38 | } 39 | ] 40 | } 41 | 42 | -------------------------------------------------------------------------------- /spec/support/responses/list_messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "start": 0, 3 | "total": 261, 4 | "num_pages": 6, 5 | "page": 0, 6 | "page_size": 50, 7 | "end": 49, 8 | "uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/SMS/Messages.json", 9 | "first_page_uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/SMS/Messages.json?Page=0&PageSize=50", 10 | "last_page_uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/SMS/Messages.json?Page=5&PageSize=50", 11 | "next_page_uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/SMS/Messages.json?Page=1&PageSize=50", 12 | "previous_page_uri": null, 13 | "sms_messages": [ 14 | { 15 | "account_sid": "AC5ef872f6da5a21de157d80997a64bd33", 16 | "api_version": "2008-08-01", 17 | "body": "Hey Jenny why aren't you returning my calls?", 18 | "date_created": "Mon, 16 Aug 2010 03:45:01 +0000", 19 | "date_sent": "Mon, 16 Aug 2010 03:45:03 +0000", 20 | "date_updated": "Mon, 16 Aug 2010 03:45:03 +0000", 21 | "direction": "outbound-api", 22 | "from": "+14158141829", 23 | "price": "-0.02000", 24 | "sid": "SM800f449d0399ed014aae2bcc0cc2f2ec", 25 | "status": "sent", 26 | "to": "+14159978453", 27 | "uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/SMS/Messages/SM800f449d0399ed014aae2bcc0cc2f2ec.json" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /spec/support/responses/list_notifications.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 0, 3 | "num_pages": 25, 4 | "page_size": 50, 5 | "total": 1224, 6 | "start": 0, 7 | "end": 49, 8 | "uri": "\/2010-04-01\/Accounts\/ACda6f1e11047ebd6fe7a55f120be3a900\/Notifications.json", 9 | "first_page_uri": "\/2010-04-01\/Accounts\/ACda6f1e11047ebd6fe7a55f120be3a900\/Notifications.json?Page=0&PageSize=50", 10 | "previous_page_uri": null, 11 | "next_page_uri": "\/2010-04-01\/Accounts\/ACda6f1e11047ebd6fe7a55f120be3a900\/Notifications.json?Page=1&PageSize=50", 12 | "last_page_uri": "\/2010-04-01\/Accounts\/ACda6f1e11047ebd6fe7a55f120be3a900\/Notifications.json?Page=24&PageSize=50", 13 | "notifications": [ 14 | { 15 | "sid": "NO5a7a84730f529f0a76b3e30c01315d1a", 16 | "account_sid": "ACda6f1e11047ebd6fe7a55f120be3a900", 17 | "call_sid": "CAa8857b0dcc71b4909aced594f7f87453", 18 | "log": "0", 19 | "error_code": "11205", 20 | "more_info": "http:\/\/www.twilio.com\/docs\/errors\/11205", 21 | "message_text": "EmailNotification=false&LogLevel=ERROR&sourceComponent=13400&Msg=HTTP+Connection+Failure+-+Read+timed+out&ErrorCode=11205&msg=HTTP+Connection+Failure+-+Read+timed+out&url=4min19secs.mp3", 22 | "message_date": "Tue, 09 Feb 2010 01:23:53 +0000", 23 | "request_method": "POST", 24 | "request_url": "http:\/\/demo.twilio.com\/welcome", 25 | "date_created": "Tue, 09 Feb 2010 01:23:53 +0000", 26 | "api_version": "2008-08-01", 27 | "date_updated": "Tue, 09 Feb 2010 01:23:53 +0000", 28 | "uri": "\/2010-04-01\/Accounts\/ACda6f1e11047ebd6fe7a55f120be3a900\/Notifications\/NO5a7a84730f529f0a76b3e30c01315d1a.json" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /spec/support/responses/list_participants.json: -------------------------------------------------------------------------------- 1 | { 2 | "num_pages": 1, 3 | "page": 0, 4 | "page_size": 50, 5 | "start": 0, 6 | "total": 2, 7 | "end": 1, 8 | "first_page_uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants.json?Page=0&PageSize=50", 9 | "last_page_uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants.json?Page=0&PageSize=50", 10 | "next_page_uri": null, 11 | "previous_page_uri": null, 12 | "uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants.json", 13 | "participants": [ 14 | { 15 | "account_sid": "AC5ef872f6da5a21de157d80997a64bd33", 16 | "call_sid": "CA386025c9bf5d6052a1d1ea42b4d16662", 17 | "conference_sid": "CFbbe46ff1274e283f7e3ac1df0072ab39", 18 | "date_created": "Wed, 18 Aug 2010 20:20:10 +0000", 19 | "date_updated": "Wed, 18 Aug 2010 20:20:10 +0000", 20 | "end_conference_on_exit": true, 21 | "muted": false, 22 | "start_conference_on_enter": true, 23 | "uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants/CA386025c9bf5d6052a1d1ea42b4d16662.json" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /spec/support/responses/list_queues.json: -------------------------------------------------------------------------------- 1 | { 2 | "first_page_uri": "/2010-04-01/Accounts/ACdc5f1e11047ebd6fe7a55f120be3a900/Queues.json?Page=0&PageSize=50", 3 | "num_pages": 1, 4 | "previous_page_uri": null, 5 | "queues": [ 6 | { 7 | "sid": "QU14fbe51706f6ef1e4756afXXbed88055", 8 | "friendly_name": "switchboard", 9 | "current_size": 0, 10 | "average_wait_time": 0, 11 | "max_size": 40, 12 | "date_created": "Wed, 10 Apr 2013 01:36:39 +0000", 13 | "date_updated": "Wed, 10 Apr 2013 01:36:39 +0000", 14 | "uri": "/2010-04-01/Accounts/ACdc5f1e11047ebd6fe7a55f120be3a900/Queues/QU14fbe51706f6ef1e4756afXXbed88055.json" 15 | }, 16 | { 17 | "sid": "QUc74cc4eedc52e282f81dXXbafddb0e9c", 18 | "friendly_name": "support", 19 | "current_size": 0, 20 | "average_wait_time": 0, 21 | "max_size": 100, 22 | "date_created": "Wed, 10 Apr 2013 00:01:27 +0000", 23 | "date_updated": "Wed, 10 Apr 2013 00:01:27 +0000", 24 | "uri": "/2010-04-01/Accounts/ACdc5f1e11047ebd6fe7a55f120be3a900/Queues/QUc74cc4eedc52e282f81dXXbafddb0e9c.json" 25 | } 26 | ], 27 | "uri": "/2010-04-01/Accounts/ACdc5f1e11047ebd6fe7a55f120be3a900/Queues.json", 28 | "page_size": 50, 29 | "start": 0, 30 | "next_page_uri": null, 31 | "end": 1, 32 | "total": 2, 33 | "last_page_uri": "/2010-04-01/Accounts/ACdc5f1e11047ebd6fe7a55f120be3a900/Queues.json?Page=0&PageSize=50", 34 | "page": 0 35 | } -------------------------------------------------------------------------------- /spec/support/responses/list_recordings.json: -------------------------------------------------------------------------------- 1 | { 2 | "start": 0, 3 | "end": 49, 4 | "total": 527, 5 | "num_pages": 11, 6 | "page": 0, 7 | "page_size": 50, 8 | "uri": "/2010-04-01/Accounts/ACda6f1e11047ebd6fe7a55f120be3a900/Recordings.json", 9 | "first_page_uri": "/2010-04-01/Accounts/ACda6f1e11047ebd6fe7a55f120be3a900/Recordings.json?Page=0&PageSize=50", 10 | "last_page_uri": "/2010-04-01/Accounts/ACda6f1e11047ebd6fe7a55f120be3a900/Recordings.json?Page=10&PageSize=50", 11 | "previous_page_uri": null, 12 | "next_page_uri": "/2010-04-01/Accounts/ACda6f1e11047ebd6fe7a55f120be3a900/Recordings.json?Page=1&PageSize=50", 13 | "recordings": [ 14 | { 15 | "account_sid": "ACda6f1e11047ebd6fe7a55f120be3a900", 16 | "api_version": "2008-08-01", 17 | "call_sid": "CA8dfedb55c129dd4d6bd1f59af9d11080", 18 | "date_created": "Fri, 17 Jul 2009 01:52:49 +0000", 19 | "date_updated": "Fri, 17 Jul 2009 01:52:49 +0000", 20 | "duration": "1", 21 | "sid": "RE557ce644e5ab84fa21cc21112e22c485", 22 | "uri": "/2010-04-01/Accounts/ACda6f1e11047ebd6fe7a55f120be3a900/Recordings/RE557ce644e5ab84fa21cc21112e22c485.json" 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /spec/support/responses/list_short_codes.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 0, 3 | "num_pages": 1, 4 | "page_size": 50, 5 | "total": 10, 6 | "start": 0, 7 | "end": 9, 8 | "uri": "/2010-04-01/Accounts/ACdc5f1e6f7a0441659833ca940b72503d/SMS/ShortCodes.json", 9 | "first_page_uri": "/2010-04-01/Accounts/ACdc5f1e6f7a0441659833ca940b72503d/SMS/ShortCodes.json?Page=0&PageSize=50", 10 | "previous_page_uri": null, 11 | "next_page_uri": null, 12 | "last_page_uri": "/2010-04-01/Accounts/ACdc5f1e6f7a0441659833ca940b72503d/SMS/ShortCodes.json?Page=0&PageSize=50", 13 | "short_codes": [ 14 | { 15 | "sid": "SC6b20cb705c1e8f00210049b20b70fce2", 16 | "account_sid": "ACdc5f1e6f7a0441659833ca940b72503d", 17 | "friendly_name": "67898", 18 | "short_code": "67898", 19 | "date_created": "Sat, 09 Jul 2011 22:36:22 +0000", 20 | "date_updated": "Wed, 13 Jul 2011 02:07:05 +0000", 21 | "sms_url": "http://myapp.com/awesome", 22 | "sms_method": "POST", 23 | "sms_fallback_url": "http://smsapp.com/fallback", 24 | "sms_fallback_method": "GET", 25 | "uri": "/2010-04-01/Accounts/ACdc5f1e6f7a0441659833ca940b72503d/SMS/ShortCodes/SC6b20cb705c1e8f00210049b20b70fce2.json" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /spec/support/responses/list_transcriptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "end": 49, 3 | "num_pages": 3, 4 | "page": 0, 5 | "page_size": 50, 6 | "previous_page_uri": null, 7 | "start": 0, 8 | "total": 150, 9 | "first_page_uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Transcriptions.json?Page=0&PageSize=50", 10 | "last_page_uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Transcriptions.json?Page=2&PageSize=50", 11 | "next_page_uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Transcriptions.json?Page=1&PageSize=50", 12 | "uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Transcriptions.json", 13 | "transcriptions": [ 14 | { 15 | "account_sid": "AC5ef872f6da5a21de157d80997a64bd33", 16 | "api_version": "2010-04-01", 17 | "date_created": "Mon, 26 Jul 2010 00:09:58 +0000", 18 | "date_updated": "Mon, 26 Jul 2010 00:10:25 +0000", 19 | "duration": "6", 20 | "price": "-0.05000", 21 | "recording_sid": "REca11f06dc31b5515a2dfb1f5134361f2", 22 | "sid": "TR8c61027b709ffb038236612dc5af8723", 23 | "status": "completed", 24 | "transcription_text": "Tommy? Tommy is that you? I told you never to call me again.", 25 | "type": "fast", 26 | "uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Transcriptions/TR8c61027b709ffb038236612dc5af8723.json" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /spec/support/responses/muted_participant.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_sid": "AC5ef872f6da5a21de157d80997a64bd33", 3 | "call_sid": "CA386025c9bf5d6052a1d1ea42b4d16662", 4 | "conference_sid": "CFbbe46ff1274e283f7e3ac1df0072ab39", 5 | "date_created": "Wed, 18 Aug 2010 20:20:10 +0000", 6 | "date_updated": "Wed, 18 Aug 2010 20:20:10 +0000", 7 | "end_conference_on_exit": true, 8 | "muted": true, 9 | "start_conference_on_enter": true, 10 | "uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants/CA386025c9bf5d6052a1d1ea42b4d16662.json" 11 | } -------------------------------------------------------------------------------- /spec/support/responses/notification.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "NO5a7a84730f529f0a76b3e30c01315d1a", 3 | "account_sid": "ACda6f1e11047ebd6fe7a55f120be3a900", 4 | "call_sid": "CAa8857b0dcc71b4909aced594f7f87453", 5 | "log": "0", 6 | "error_code": "11205", 7 | "more_info": "http:\/\/www.twilio.com\/docs\/errors\/11205", 8 | "message_text": "EmailNotification=false&LogLevel=ERROR&sourceComponent=13400&Msg=HTTP+Connection+Failure+-+Read+timed+out&ErrorCode=11205&msg=HTTP+Connection+Failure+-+Read+timed+out&url=4min19secs.mp3", 9 | "message_date": "Tue, 09 Feb 2010 01:23:53 +0000", 10 | "response_body": "\n\n\t4min19secs.mp3<\/Play>\n<\/Response>\n", 11 | "request_method": "GET", 12 | "request_url": "http:\/\/demo.twilio.com\/welcome", 13 | "request_variables": "AccountSid=ACda6f1e11047ebd6fe7a55f120be3a900&CallStatus=in-progress&Called=4152374451&CallerCountry=US&CalledZip=94937&CallerCity=&Caller=4150000000&CalledCity=INVERNESS&CalledCountry=US&DialStatus=answered&CallerState=California&CallSid=CAa8857b0dcc71b4909aced594f7f87453&CalledState=CA&CallerZip=", 14 | "response_headers": "Date=Tue%2C+09+Feb+2010+01%3A23%3A38+GMT&Vary=Accept-Encoding&Content-Length=91&Content-Type=text%2Fxml&Accept-Ranges=bytes&Server=Apache%2F2.2.3+%28CentOS%29&X-Powered-By=PHP%2F5.1.6", 15 | "date_created": "Tue, 09 Feb 2010 01:23:53 +0000", 16 | "api_version": "2008-08-01", 17 | "date_updated": "Tue, 09 Feb 2010 01:23:53 +0000", 18 | "uri": "\/2010-04-01\/Accounts\/ACda6f1e11047ebd6fe7a55f120be3a900\/Notifications\/NO5a7a84730f529f0a76b3e30c01315d1a.json" 19 | } -------------------------------------------------------------------------------- /spec/support/responses/queue.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "QU14fbe51706f6ef1e4756afXXbed88055", 3 | "friendly_name": "switchboard", 4 | "current_size": 0, 5 | "average_wait_time": 0, 6 | "max_size": 40, 7 | "date_created": "Wed, 10 Apr 2013 01:36:39 +0000", 8 | "date_updated": "Wed, 10 Apr 2013 01:36:39 +0000", 9 | "uri": "/2010-04-01/Accounts/ACdc5f1e11047ebd6fe7a55f120be3a900/Queues/QU14fbe51706f6ef1e4756af99bed88055.json" 10 | } -------------------------------------------------------------------------------- /spec/support/responses/recording.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_sid": "ACda6f1e11047ebd6fe7a55f120be3a900", 3 | "api_version": "2008-08-01", 4 | "call_sid": "CA8dfedb55c129dd4d6bd1f59af9d11080", 5 | "date_created": "Fri, 17 Jul 2009 01:52:49 +0000", 6 | "date_updated": "Fri, 17 Jul 2009 01:52:49 +0000", 7 | "duration": "1", 8 | "sid": "RE557ce644e5ab84fa21cc21112e22c485", 9 | "uri": "/2010-04-01/Accounts/ACda6f1e11047ebd6fe7a55f120be3a900/Recordings/RE557ce644e5ab84fa21cc21112e22c485.json" 10 | } -------------------------------------------------------------------------------- /spec/support/responses/sandbox.json: -------------------------------------------------------------------------------- 1 | { 2 | "pin": "63729915", 3 | "account_sid": "ACda6f1e31047ebd6fe7a55f120be3a900", 4 | "phone_number": "+14154132865", 5 | "voice_url": "http:\/\/demo.twilio.com\/welcome", 6 | "voice_method": "GET", 7 | "sms_url": "http:\/\/demo.twilio.com\/welcome\/sms", 8 | "sms_method": "POST", 9 | "date_created": "Mon, 10 Nov 2008 20:52:56 +0000", 10 | "date_updated": "Mon, 10 Nov 2008 20:52:56 +0000", 11 | "api_version": "2010-04-01", 12 | "uri": "\/2010-04-01\/Accounts\/ACda6f1e31047ebd6fe7a55f120be3a900\/Sandbox.json" 13 | } -------------------------------------------------------------------------------- /spec/support/responses/short_code.json: -------------------------------------------------------------------------------- 1 | { 2 | "sid": "SC6b20cb705c1e8f00210049b20b70fce2", 3 | "account_sid": "ACdc5f1e6f7a0441659833ca940b72503d", 4 | "friendly_name": "67898", 5 | "short_code": "67898", 6 | "date_created": "Sat, 09 Jul 2011 22:36:22 +0000", 7 | "date_updated": "Sat, 09 Jul 2011 22:36:22 +0000", 8 | "sms_url": "http://smsapp.com/sms", 9 | "sms_method": "POST", 10 | "sms_fallback_url": "http://smsapp.com/fallback", 11 | "sms_fallback_method": "GET", 12 | "uri": "/2010-04-01/Accounts/ACdc5f1e6f7a0441659833ca940b72503d/SMS/ShortCodes/SC6b20cb705c1e8f00210049b20b70fce2.json" 13 | } 14 | -------------------------------------------------------------------------------- /spec/support/responses/show_connect_participant.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_sid": "AC0000000000000000000000000000", 3 | "connect_app_sid": "CN5ef872f6da5a21de157d80997a64bd33", 4 | "call_sid": "CA386025c9bf5d6052a1d1ea42b4d16662", 5 | "conference_sid": "CFbbe46ff1274e283f7e3ac1df0072ab39", 6 | "date_created": "Wed, 18 Aug 2010 20:20:10 +0000", 7 | "date_updated": "Wed, 18 Aug 2010 20:20:10 +0000", 8 | "end_conference_on_exit": true, 9 | "muted": false, 10 | "start_conference_on_enter": true, 11 | "uri": "/2010-04-01/Accounts/AC0000000000000000000000000000/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants/CA386025c9bf5d6052a1d1ea42b4d16662.json" 12 | } 13 | -------------------------------------------------------------------------------- /spec/support/responses/show_participant.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_sid": "AC5ef872f6da5a21de157d80997a64bd33", 3 | "call_sid": "CA386025c9bf5d6052a1d1ea42b4d16662", 4 | "conference_sid": "CFbbe46ff1274e283f7e3ac1df0072ab39", 5 | "date_created": "Wed, 18 Aug 2010 20:20:10 +0000", 6 | "date_updated": "Wed, 18 Aug 2010 20:20:10 +0000", 7 | "end_conference_on_exit": true, 8 | "muted": false, 9 | "start_conference_on_enter": true, 10 | "uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Conferences/CFbbe46ff1274e283f7e3ac1df0072ab39/Participants/CA386025c9bf5d6052a1d1ea42b4d16662.json" 11 | } -------------------------------------------------------------------------------- /spec/support/responses/sms_created.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_sid": "AC5ef872f6da5a21de157d80997a64bd33", 3 | "api_version": "2010-04-01", 4 | "body": "Jenny please?! I love you <3", 5 | "date_created": "Wed, 18 Aug 2010 20:01:40 +0000", 6 | "date_sent": null, 7 | "date_updated": "Wed, 18 Aug 2010 20:01:40 +0000", 8 | "direction": "outbound-api", 9 | "from": "+14158141829", 10 | "price": null, 11 | "sid": "SM90c6fc909d8504d45ecdb3a3d5b3556e", 12 | "status": "queued", 13 | "to": "+14159352345", 14 | "uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/SMS/Messages/SM90c6fc909d8504d45ecdb3a3d5b3556e.json" 15 | } -------------------------------------------------------------------------------- /spec/support/responses/transcription.json: -------------------------------------------------------------------------------- 1 | { 2 | "account_sid": "AC5ef872f6da5a21de157d80997a64bd33", 3 | "api_version": "2008-08-01", 4 | "date_created": "Mon, 26 Jul 2010 00:09:58 +0000", 5 | "date_updated": "Mon, 26 Jul 2010 00:10:25 +0000", 6 | "duration": "6", 7 | "price": "-0.05000", 8 | "recording_sid": "REca11f06dc31b5515a2dfb1f5134361f2", 9 | "sid": "TR8c61027b709ffb038236612dc5af8723", 10 | "status": "completed", 11 | "transcription_text": "Tommy? Tommy is that you? I told you never to call me again.", 12 | "type": "fast", 13 | "uri": "/2010-04-01/Accounts/AC5ef872f6da5a21de157d80997a64bd33/Transcriptions/TR8c61027b709ffb038236612dc5af8723.json" 14 | } -------------------------------------------------------------------------------- /spec/transcription_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Twilio::Transcription do 4 | 5 | before { Twilio::Config.setup :account_sid => 'AC000000000000', :auth_token => '79ad98413d911947f0ba369d295ae7a3' } 6 | 7 | def resource_uri(account_sid=nil, connect=nil) 8 | account_sid ||= Twilio::Config.account_sid 9 | "https://#{connect ? account_sid : Twilio::Config.account_sid}:#{Twilio::Config.auth_token}@api.twilio.com/2010-04-01/Accounts/#{account_sid}/Transcriptions" 10 | end 11 | 12 | def stub_api_call(response_file, account_sid=nil) 13 | stub_request(:get, resource_uri(account_sid) + '.json'). 14 | to_return :body => canned_response(response_file), :status => 200 15 | end 16 | 17 | describe '.all' do 18 | context 'using a twilio connect subaccount' do 19 | it 'uses the account sid as the username for basic auth' do 20 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 21 | to_return :body => canned_response('list_connect_transcriptions'), :status => 200 22 | Twilio::Transcription.all :account_sid => 'AC0000000000000000000000000000', :connect => true 23 | end 24 | end 25 | 26 | before { stub_api_call 'list_transcriptions' } 27 | it 'returns a collection of objects with a length corresponding to the response' do 28 | resp = Twilio::Transcription.all 29 | resp.length.should == 1 30 | end 31 | 32 | it 'returns a collection containing instances of Twilio::Transcription' do 33 | resp = Twilio::Transcription.all 34 | resp.all? { |r| r.is_a? Twilio::Transcription }.should be_true 35 | end 36 | 37 | JSON.parse(canned_response('list_transcriptions'))['transcriptions'].each_with_index do |obj,i| 38 | obj.each do |attr, value| 39 | specify { Twilio::Transcription.all[i].send(attr).should == value } 40 | end 41 | end 42 | 43 | context 'on a subaccount' do 44 | before { stub_api_call 'list_transcriptions', 'SUBACCOUNT_SID' } 45 | 46 | context 'found by passing in an account_sid' do 47 | it 'uses the subaccount sid in the request' do 48 | Twilio::Transcription.all :account_sid => 'SUBACCOUNT_SID' 49 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 50 | end 51 | end 52 | 53 | context 'found by passing in an instance of Twilio::Account' do 54 | it 'uses the subaccount sid in the request' do 55 | Twilio::Transcription.all :account => mock(:sid => 'SUBACCOUNT_SID') 56 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 57 | end 58 | end 59 | end 60 | end 61 | 62 | describe '.count' do 63 | context 'using a twilio connect subaccount' do 64 | it 'uses the account sid as the username for basic auth' do 65 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '.json' ). 66 | to_return :body => canned_response('list_connect_transcriptions'), :status => 200 67 | Twilio::Transcription.count :account_sid => 'AC0000000000000000000000000000', :connect => true 68 | end 69 | end 70 | 71 | before { stub_api_call 'list_transcriptions' } 72 | it 'returns the number of resources' do 73 | Twilio::Transcription.count.should == 150 74 | end 75 | 76 | context 'on a subaccount' do 77 | before { stub_api_call 'list_transcriptions', 'SUBACCOUNT_SID' } 78 | 79 | context 'found by passing in an account_sid' do 80 | it 'uses the subaccount sid in the request' do 81 | Twilio::Transcription.count :account_sid => 'SUBACCOUNT_SID' 82 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 83 | end 84 | end 85 | 86 | context 'found by passing in an instance of Twilio::Account' do 87 | it 'uses the subaccount sid in the request' do 88 | Twilio::Transcription.count :account => mock(:sid => 'SUBACCOUNT_SID') 89 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '.json').should have_been_made 90 | end 91 | end 92 | end 93 | end 94 | 95 | describe '.find' do 96 | context 'using a twilio connect subaccount' do 97 | it 'uses the account sid as the username for basic auth' do 98 | stub_request(:get, resource_uri('AC0000000000000000000000000000', true) + '/TR8c61027b709ffb038236612dc5af8723.json' ). 99 | to_return :body => canned_response('connect_transcription'), :status => 200 100 | Twilio::Transcription.find 'TR8c61027b709ffb038236612dc5af8723', :account_sid => 'AC0000000000000000000000000000', :connect => true 101 | end 102 | end 103 | context 'for a valid transcription' do 104 | before do 105 | stub_request(:get, resource_uri + '/TR8c61027b709ffb038236612dc5af8723' + '.json'). 106 | to_return :body => canned_response('transcription'), :status => 200 107 | end 108 | let(:transcription) { Twilio::Transcription.find 'TR8c61027b709ffb038236612dc5af8723' } 109 | 110 | it 'returns an instance of Twilio::Transcription.all' do 111 | transcription.should be_a Twilio::Transcription 112 | end 113 | 114 | JSON.parse(canned_response('transcription')).each do |k,v| 115 | specify { transcription.send(k).should == v } 116 | end 117 | end 118 | 119 | context 'for a string that does not correspond to a real transcription' do 120 | before do 121 | stub_request(:get, resource_uri + '/phony' + '.json').to_return :status => 404 122 | end 123 | it 'returns nil' do 124 | transcription = Twilio::Transcription.find 'phony' 125 | transcription.should be_nil 126 | end 127 | end 128 | 129 | context 'on a subaccount' do 130 | before do 131 | stub_request(:get, resource_uri('SUBACCOUNT_SID') + '/TR8c61027b709ffb038236612dc5af8723' + '.json'). 132 | to_return :body => canned_response('notification'), :status => 200 133 | end 134 | 135 | context 'found by passing in an account_sid' do 136 | it 'uses the subaccount sid in the request' do 137 | Twilio::Transcription.find 'TR8c61027b709ffb038236612dc5af8723', :account_sid => 'SUBACCOUNT_SID' 138 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/TR8c61027b709ffb038236612dc5af8723' + '.json'). 139 | should have_been_made 140 | end 141 | end 142 | 143 | context 'found by passing in an instance of Twilio::Account' do 144 | it 'uses the subaccount sid in the request' do 145 | Twilio::Transcription.find 'TR8c61027b709ffb038236612dc5af8723', :account => mock(:sid => 'SUBACCOUNT_SID') 146 | a_request(:get, resource_uri('SUBACCOUNT_SID') + '/TR8c61027b709ffb038236612dc5af8723' + '.json'). 147 | should have_been_made 148 | end 149 | end 150 | end 151 | end 152 | end 153 | -------------------------------------------------------------------------------- /spec/twiml_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper') 2 | 3 | describe Twilio::TwiML do 4 | describe ".build" do 5 | it 'starts with the XML doctype' do 6 | Twilio::TwiML.build.should =~ /^<\?xml version="1.0" encoding="UTF-8"\?>/ 7 | end 8 | it 'capitalises method names before conversion into XML elements so that one can write nice code' do 9 | xml = Twilio::TwiML.build do |res| 10 | res.say 'Hey man! Listen to this!', :voice => 'man' 11 | end 12 | xml.should == "\n\n " + 13 | "Hey man! Listen to this!\n\n" 14 | end 15 | it 'camelizes XML element attributes before conversion into XML elements so that one can write nice code' do 16 | xml = Twilio::TwiML.build do |res| 17 | res.record :action => "http://foo.com/handleRecording.php", :method => "GET", :max_length => "20", :finish_on_Key => "*" 18 | end 19 | xml.should == "\n\n " + 20 | "\n\n" 21 | end 22 | it 'works with nested elements' do 23 | xml = Twilio::TwiML.build do |res| 24 | res.gather :action => "/process_gather.php", :method => "GET" do |g| 25 | g.say 'Now hit some buttons!' 26 | end 27 | end 28 | xml.should == "\n\n " + 29 | "\n Now hit some buttons!\n " + 30 | "\n\n" 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /twilio-rb.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.platform = Gem::Platform::RUBY 3 | s.name = 'twilio-rb' 4 | s.version = '2.3.0' 5 | s.summary = 'Interact with the Twilio API in a nice Ruby way.' 6 | s.description = 'A nice Ruby wrapper for the Twilio REST API' 7 | 8 | s.required_ruby_version = '>= 1.8.7' 9 | 10 | s.author = 'Stevie Graham' 11 | s.email = 'sjtgraham@mac.com' 12 | s.homepage = 'http://github.com/stevegraham/twilio-rb' 13 | 14 | s.add_dependency 'activesupport', '>= 3.0.0' 15 | s.add_dependency 'i18n', '~> 0.5' 16 | s.add_dependency 'httparty', '>= 0.10.0' 17 | s.add_dependency 'crack', '~> 0.3.2' 18 | s.add_dependency 'builder', '>= 3.0.0' 19 | s.add_dependency 'jwt', '>= 0.1.3' 20 | 21 | s.add_development_dependency 'webmock', '>= 1.6.1' 22 | s.add_development_dependency 'rspec', '>= 2.2.0' 23 | s.add_development_dependency 'mocha', '>= 0.9.10' 24 | s.add_development_dependency 'timecop', '>= 0.3.5' 25 | s.add_development_dependency 'rake', '~> 10.1.0' 26 | 27 | s.files = Dir['README.md', 'lib/**/*'] 28 | s.require_path = 'lib' 29 | end 30 | --------------------------------------------------------------------------------