nexmos 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3@nexmos --create
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - ruby-head
6
+ - jruby-19mode # JRuby in 1.9 mode
7
+ - rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in nexmo-client.gemspec
4
+ gemspec
5
+ group :development, :test do
6
+ gem 'pry'
7
+ gem 'rake'
8
+ gem 'webmock'
9
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Alexander Simonov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,67 @@
1
+ # Nexmos [![Build Status](https://travis-ci.org/phenomena/nexmos.png)](https://travis-ci.org/phenomena/nexmos)[![Code Climate](https://codeclimate.com/github/phenomena/nexmos.png)](https://codeclimate.com/github/phenomena/nexmos)
2
+
3
+ Nexmo API wrapper
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'nexmos'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install nexmos
18
+
19
+ ## Usage
20
+
21
+ ### Send text message
22
+
23
+ ```ruby
24
+ # Nexmos specific client
25
+ client = ::Nexmos::Message.new('api-key', 'api-secret')
26
+ # get result from Nexmo
27
+ res = client.send_text(from: 'your number', to: '+1234567890', text: 'Hello world!')
28
+ # check if send is success
29
+ if res.success?
30
+ puts "ok"
31
+ else
32
+ puts "fail"
33
+ end
34
+ ```
35
+
36
+ ### Get balance
37
+
38
+ ```ruby
39
+ client = ::Nexmos::Account.new('api-key', 'api-secret')
40
+ res = client.get_balance
41
+ my_balance = res.value
42
+ ```
43
+
44
+ ## Rails integration
45
+
46
+ You can create `config/initializer/nexmos.rb` file with next content:
47
+
48
+ ```ruby
49
+ Nexmos.setup do |n|
50
+ n.api_key = 'api_key'
51
+ n.api_secret = 'api_secret'
52
+ end
53
+ ```
54
+
55
+ And then you can call all clients without providing key and secret.
56
+
57
+ ## More details about api calls
58
+
59
+ More details about api calls you can found in the lib/api.yml file.
60
+
61
+ ## Contributing
62
+
63
+ 1. Fork it
64
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
65
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
66
+ 4. Push to the branch (`git push origin my-new-feature`)
67
+ 5. Create new Pull Request
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,113 @@
1
+ :message:
2
+ :send_text:
3
+ :method: :get
4
+ :url: /sms/json
5
+ :required:
6
+ - from
7
+ - to
8
+ - text
9
+ :send_binary:
10
+ :method: :get
11
+ :url: /sms/json
12
+ :required:
13
+ - body
14
+ - udh
15
+ :params:
16
+ type: binary
17
+ :wap_push:
18
+ :method: :get
19
+ :url: /sms/json
20
+ :required:
21
+ - title
22
+ - url
23
+ :optional:
24
+ - validity
25
+ :params:
26
+ type: wappush
27
+ :search:
28
+ :message:
29
+ :method: :get
30
+ :url: /search/message
31
+ :required:
32
+ - id
33
+ :messages:
34
+ :method: :get
35
+ :url: /search/messages
36
+ :optional:
37
+ - ids
38
+ - date
39
+ - to
40
+ :rejections:
41
+ :method: :get
42
+ :url: /search/rejections
43
+ :required:
44
+ - date
45
+ :optional:
46
+ - to
47
+ :account:
48
+ :get_balance:
49
+ :method: :get
50
+ :url: /account/get-balance
51
+ :return: value
52
+ :get_pricing:
53
+ :method: :get
54
+ :url: /account/get-pricing/outbound
55
+ :required:
56
+ - country
57
+ :get_prefix_pricing:
58
+ :method: :get
59
+ :url: /account/get-prefix-pricing/outbound
60
+ :required:
61
+ - prefix
62
+ :settings:
63
+ :method: :post
64
+ :url: /account/settings
65
+ :optional:
66
+ - new_secret
67
+ - mo_call_back_url
68
+ - dr_call_back_url
69
+ :camelize: true
70
+ :get_numbers:
71
+ :method: :get
72
+ :url: /account/numbers
73
+ :optional:
74
+ - index
75
+ - size
76
+ - pattern
77
+ :top_up:
78
+ :method: :get
79
+ :url: /account/top-up
80
+ :required:
81
+ - trx
82
+ :number:
83
+ :search:
84
+ :method: :get
85
+ :url: /number/search
86
+ :required:
87
+ - country
88
+ :optional:
89
+ - pattern
90
+ - index
91
+ - size
92
+ :buy:
93
+ :method: :post
94
+ :url: /number/buy
95
+ :required:
96
+ - country
97
+ - msisdn
98
+ :cancel:
99
+ :method: :post
100
+ :url: /number/cancel
101
+ :required:
102
+ - country
103
+ - msisdn
104
+ :update:
105
+ :method: :post
106
+ :url: /number/update
107
+ :required:
108
+ - country
109
+ - msisdn
110
+ :optional:
111
+ - mo_http_url
112
+ - mo_smpp_sys_type
113
+ :camelize: true
@@ -0,0 +1,47 @@
1
+ require 'logger'
2
+ require 'active_support/core_ext/hash'
3
+ require 'faraday'
4
+ require 'faraday_middleware'
5
+ require 'nexmos/version'
6
+ require 'nexmos/base'
7
+ module Nexmos
8
+ extend self
9
+ attr_accessor :api_key, :api_secret, :debug
10
+ attr_writer :user_agent, :logger
11
+
12
+ # ensures the setup only gets run once
13
+ @_ran_once = false
14
+
15
+ def reset!
16
+ @logger = nil
17
+ @_ran_once = false
18
+ @user_agent = nil
19
+ @api_key = nil
20
+ @api_secret = nil
21
+ end
22
+
23
+ def user_agent
24
+ @user_agent ||= "Nexmos v#{::Nexmos::VERSION}"
25
+ end
26
+
27
+ def setup
28
+ yield self unless @_ran_once
29
+ @_ran_once = true
30
+ end
31
+
32
+ def logger
33
+ @logger ||= Logger.new(STDOUT)
34
+ end
35
+
36
+ def apis
37
+ @apis ||= YAML.load_file(File.expand_path('api.yml', File.dirname(__FILE__)))
38
+ end
39
+
40
+ reset!
41
+ end
42
+
43
+ require 'nexmos/railties' if defined?(::Rails)
44
+ require 'nexmos/account'
45
+ require 'nexmos/message'
46
+ require 'nexmos/number'
47
+ require 'nexmos/search'
@@ -0,0 +1,5 @@
1
+ module Nexmos
2
+ class Account < Base
3
+ self.define_api_calls(:account)
4
+ end
5
+ end
@@ -0,0 +1,103 @@
1
+ module Nexmos
2
+ class Base
3
+
4
+ def initialize(key = ::Nexmos.api_key, secret = ::Nexmos.api_secret)
5
+ raise 'api_key should be set' if !key.present?
6
+ raise 'api_secret should be set' if !secret.present?
7
+ @default_params = {
8
+ 'api_key' => key,
9
+ 'api_secret' => secret
10
+ }
11
+ end
12
+
13
+ def connection
14
+ self.class.connection.dup
15
+ end
16
+
17
+ def make_api_call(args, params = {})
18
+ normalize_params(params)
19
+ check_required_params(args, params)
20
+ camelize_params(params) if args[:camelize]
21
+ params.merge!(@default_params)
22
+ get_response(args, params)
23
+ end
24
+
25
+ def get_response(args, params)
26
+ method = args[:method]
27
+ url = args[:url]
28
+ raise 'url or method params missing' if !method.present? || !url.present?
29
+ res = connection.__send__(method, url, params)
30
+ if res.success?
31
+ data = if res.body.is_a?(::Hash)
32
+ res.body.merge(:success? => true)
33
+ else
34
+ ::Hashie::Mash.new({:success? => true})
35
+ end
36
+ return data
37
+ end
38
+ failed_res = ::Hashie::Mash.new({:success? => false, :not_authorized? => false, :failed? => false})
39
+ case res.status
40
+ when 401
41
+ failed_res.merge! :not_authorized? => true
42
+ when 420
43
+ failed_res.merge! :failed? => true
44
+ end
45
+ failed_res
46
+ end
47
+
48
+ def normalize_params(params)
49
+ params.stringify_keys!
50
+ end
51
+
52
+ def camelize_params(params)
53
+ if params.respond_to?(:transform_keys!)
54
+ params.transform_keys!{|key| key.camelize(:lower)}
55
+ else
56
+ params.keys.each do |key|
57
+ params[key.camelize(:lower)] = params.delete(key)
58
+ end
59
+ end
60
+ end
61
+
62
+ def check_required_params(args, params)
63
+ if args[:required]
64
+ required = params.slice(*args[:required])
65
+ unless required.keys.sort == args[:required].sort
66
+ missed = (args[:required] - required.keys).join(',')
67
+ raise ArgumentError, "#{missed} params required"
68
+ end
69
+ end
70
+ end
71
+
72
+ class << self
73
+
74
+ def define_api_calls(key)
75
+ ::Nexmos.apis[key].each do |k,v|
76
+ define_method(k) do |*args|
77
+ params = args[0] || {}
78
+ make_api_call(v, params)
79
+ end
80
+ end
81
+ end
82
+
83
+ def faraday_options
84
+ {
85
+ :url => 'https://rest.nexmo.com',
86
+ :headers => {
87
+ :accept => 'application/json',
88
+ :user_agent => ::Nexmos.user_agent
89
+ }
90
+ }
91
+ end
92
+
93
+ def connection
94
+ @connection ||= Faraday::Connection.new(faraday_options) do |conn|
95
+ conn.request :url_encoded
96
+ conn.response :rashify
97
+ conn.response :json, :content_type => /\bjson$/
98
+ conn.adapter Faraday.default_adapter
99
+ end
100
+ end
101
+ end # self
102
+ end # Base
103
+ end # Nexmos
@@ -0,0 +1,5 @@
1
+ module Nexmos
2
+ class Message < Base
3
+ self.define_api_calls(:message)
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Nexmos
2
+ class Number < Base
3
+ self.define_api_calls(:number)
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module Nexmos
2
+ class Railties < ::Rails::Railtie
3
+ initializer 'Rails logger' do
4
+ ::Nexmos.logger = Rails.logger
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module Nexmos
2
+ class Search < Base
3
+ self.define_api_calls(:search)
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module Nexmos
2
+ VERSION = "0.1"
3
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'nexmos/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "nexmos"
8
+ gem.version = Nexmos::VERSION
9
+ gem.authors = ["Alexander Simonov"]
10
+ gem.email = ["alex@simonov.me"]
11
+ gem.description = %q{Nexmo API client}
12
+ gem.summary = %q{Nexmo API client}
13
+ gem.homepage = "https://github.com/simonoff/nexmos"
14
+ gem.files = `git ls-files`.split($/)
15
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.require_paths = ["lib"]
18
+ gem.add_dependency('rash')
19
+ gem.add_dependency('faraday')
20
+ gem.add_dependency('faraday_middleware')
21
+ gem.add_dependency('activesupport', '>= 3.0.0')
22
+ gem.add_development_dependency('rspec')
23
+ gem.add_development_dependency('webmock')
24
+ end
@@ -0,0 +1,188 @@
1
+ require 'spec_helper'
2
+ describe ::Nexmos::Account do
3
+ let(:webmock_default_headers) do
4
+ {
5
+ :headers => {
6
+ 'Accept' => 'application/json',
7
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
8
+ 'User-Agent' => ::Nexmos.user_agent
9
+ }
10
+ }
11
+ end
12
+
13
+ let(:finland_prices) do
14
+ {
15
+ "mt" => "0.02500000",
16
+ "country" => "FI",
17
+ "prefix" => "358",
18
+ "networks" =>
19
+ [{"code" => "24491",
20
+ "network" => "sonera, TeleFinland",
21
+ "ranges" => ["35840", "35842", "358450"],
22
+ "mtPrice" => "0.04500000"},
23
+ {"code" => "24414",
24
+ "network" => "GSM Aland",
25
+ "ranges" => ["3584570", "3584573", "3584575"],
26
+ "mtPrice" => "0.00850000"},
27
+ {"code" => "24421",
28
+ "network" => "SAUNALAHTI, EUnet Finland",
29
+ "ranges" => ["358451", "358452", "358453", "358456", "358458"],
30
+ "mtPrice" => "0.05000000"},
31
+ {"code" => "24412",
32
+ "network" => "dna",
33
+ "ranges" =>
34
+ ["35841",
35
+ "35844",
36
+ "3584574",
37
+ "3584576",
38
+ "3584577",
39
+ "3584578",
40
+ "3584579",
41
+ "3584944"],
42
+ "mtPrice" => "0.00850000"},
43
+ {"code" => "24405",
44
+ "network" => "elisa",
45
+ "ranges" => ["35846", "35850"],
46
+ "mtPrice" => "0.05000000"}],
47
+ "name" => "Finland"
48
+ }
49
+ end
50
+
51
+ let(:prefix_prices) do
52
+ {"count" => 1,
53
+ "prices" =>
54
+ [{"mt" => "0.02500000",
55
+ "country" => "FI",
56
+ "prefix" => "358",
57
+ "networks" =>
58
+ [{"code" => "24491",
59
+ "network" => "sonera, TeleFinland",
60
+ "ranges" => nil,
61
+ "mt_price" => "0.04500000"},
62
+ {"code" => "24414",
63
+ "network" => "GSM Aland",
64
+ "ranges" => nil,
65
+ "mtPrice" => "0.00850000"},
66
+ {"code" => "24400",
67
+ "network" => "Unknown Finland",
68
+ "ranges" => nil,
69
+ "mtPrice" => "0.02500000"},
70
+ {"code" => "24421",
71
+ "network" => "SAUNALAHTI, EUnet Finland",
72
+ "ranges" => nil,
73
+ "mtPrice" => "0.05000000"},
74
+ {"code" => "24412",
75
+ "network" => "dna",
76
+ "ranges" => nil,
77
+ "mtPrice" => "0.00850000"},
78
+ {"code" => "24405",
79
+ "network" => "elisa",
80
+ "ranges" => nil,
81
+ "mtPrice" => "0.05000000"}],
82
+ "name" => "Finland"}],
83
+ }
84
+ end
85
+
86
+ before(:each) do
87
+ ::Nexmos.reset!
88
+ ::Nexmos.setup do |c|
89
+ c.api_key = 'default_key'
90
+ c.api_secret = 'default_secret'
91
+ end
92
+ end
93
+
94
+ subject { ::Nexmos::Account.new }
95
+
96
+ context '#get_balance' do
97
+
98
+ it 'should return value' do
99
+ request = stub_request(:get, "https://rest.nexmo.com/account/get-balance?api_key=default_key&api_secret=default_secret").
100
+ with(webmock_default_headers).to_return(:status => 200, :body => {"value" => 4.107}, :headers => {})
101
+ res = subject.get_balance
102
+ res.should be_kind_of(::Hash)
103
+ res.value.should == 4.107
104
+ request.should have_been_made.once
105
+ end
106
+ end
107
+
108
+ context '#get_pricing' do
109
+ it 'should return error on missed param' do
110
+ expect { subject.get_pricing }.to raise_error('country params required')
111
+ end
112
+
113
+ it 'should be success' do
114
+ request = stub_request(:get, "https://rest.nexmo.com/account/get-pricing/outbound?api_key=default_key&api_secret=default_secret&country=FI").
115
+ with(webmock_default_headers).to_return(:status => 200, :body => finland_prices, :headers => {})
116
+ res = subject.get_pricing(:country => 'FI')
117
+ res.should be_kind_of(::Hash)
118
+ res.success?.should be_true
119
+ res.country.should == 'FI'
120
+ res.keys.sort.should == %w(country name prefix mt networks success?).sort
121
+ res.networks.should be_kind_of(::Array)
122
+ res.networks[0].keys.sort.should == %w(code network mt_price ranges).sort
123
+ request.should have_been_made.once
124
+ end
125
+ end
126
+
127
+ context '#get_prefix_pricing' do
128
+ it 'should return error on missed param' do
129
+ expect { subject.get_prefix_pricing }.to raise_error('prefix params required')
130
+ end
131
+
132
+ it 'should be success' do
133
+ request = stub_request(:get, "https://rest.nexmo.com/account/get-prefix-pricing/outbound?api_key=default_key&api_secret=default_secret&prefix=358").
134
+ with(webmock_default_headers).to_return(:status => 200, :body => prefix_prices, :headers => {})
135
+ res = subject.get_prefix_pricing(:prefix => '358')
136
+ res.should be_kind_of(::Hash)
137
+ res.success?.should be_true
138
+ res.keys.sort.should == %w(count prices success?).sort
139
+ res.prices.should be_kind_of(::Array)
140
+ res.prices[0].keys.sort.should == %w(country name prefix mt networks).sort
141
+ res.prices[0].networks.should be_kind_of(::Array)
142
+ res.prices[0].networks[0].keys.sort.should == %w(code network mt_price ranges).sort
143
+ request.should have_been_made.once
144
+ end
145
+ end
146
+
147
+ context '#get_numbers' do
148
+ it 'should return only count on empty numbers' do
149
+ request = stub_request(:get, "https://rest.nexmo.com/account/numbers?api_key=default_key&api_secret=default_secret").
150
+ with(webmock_default_headers).to_return(:status => 200, :body => {:count => 0}, :headers => {})
151
+ res = subject.get_numbers
152
+ res.should be_kind_of(::Hash)
153
+ res.success?.should be_true
154
+ res['count'].should == 0
155
+ request.should have_been_made.once
156
+ end
157
+
158
+ it 'should return numbers array' do
159
+ request = stub_request(:get, "https://rest.nexmo.com/account/numbers?api_key=default_key&api_secret=default_secret").
160
+ with(webmock_default_headers).to_return(:status => 200, :body => { "count" => 1,"numbers" => [{"country" => "ES","msisdn" => "34911067000","type" => "landline"}]}, :headers => {})
161
+ res = subject.get_numbers
162
+ res.should be_kind_of(::Hash)
163
+ res.success?.should be_true
164
+ res['count'].should == 1
165
+ res['numbers'].should be_kind_of(::Array)
166
+ res['numbers'].first.should be_kind_of(::Hash)
167
+ res['numbers'].first.should == {"country" => "ES","msisdn" => "34911067000","type" => "landline"}
168
+ request.should have_been_made.once
169
+ end
170
+ end
171
+
172
+ context '#top_up' do
173
+ it 'should return error on missed param' do
174
+ expect { subject.top_up }.to raise_error('trx params required')
175
+ end
176
+
177
+ it 'should success top up' do
178
+ request = stub_request(:get, "https://rest.nexmo.com/account/top-up?api_key=default_key&api_secret=default_secret&trx=test_trx").
179
+ with(webmock_default_headers).to_return(:status => 200, :body => '', :headers => {})
180
+ res = subject.top_up :trx => 'test_trx'
181
+ res.should be_kind_of(::Hash)
182
+ res.success?.should be_true
183
+ request.should have_been_made.once
184
+ end
185
+
186
+ end
187
+
188
+ end
@@ -0,0 +1,203 @@
1
+ require 'spec_helper'
2
+ describe ::Nexmos::Base do
3
+
4
+ let(:webmock_default_headers) do
5
+ {
6
+ :headers => {
7
+ 'Accept'=>'application/json',
8
+ 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
9
+ 'User-Agent'=> ::Nexmos.user_agent
10
+ }
11
+ }
12
+ end
13
+
14
+ before(:each) do
15
+ ::Nexmos.reset!
16
+ end
17
+ context 'class' do
18
+ subject { ::Nexmos::Base }
19
+ let(:default_faraday_options) do
20
+ {
21
+ :url => 'https://rest.nexmo.com',
22
+ :headers => {
23
+ :accept => 'application/json',
24
+ :user_agent => ::Nexmos.user_agent
25
+ }
26
+ }
27
+ end
28
+
29
+ its(:faraday_options) { should == default_faraday_options }
30
+ its(:connection) { should be_kind_of(::Faraday::Connection) }
31
+
32
+ context 'faraday_options' do
33
+ it 'should have custom user agent' do
34
+ ::Nexmos.setup do |c|
35
+ c.user_agent = 'test user agent'
36
+ end
37
+ default_faraday_options[:headers][:user_agent] = 'test user agent'
38
+ subject.faraday_options.should == default_faraday_options
39
+ end
40
+ end
41
+
42
+ context 'define_api_calls' do
43
+ it 'should call define_method' do
44
+ ::Nexmos.apis[:account].keys.each do |k|
45
+ subject.should_receive(:define_method).with(k)
46
+ end
47
+ subject.define_api_calls(:account)
48
+ end
49
+
50
+ it 'should define dynamic method and call make_api_call inside' do
51
+ subject.define_api_calls(:account)
52
+ instance = subject.new('test_key', 'test_secret')
53
+ instance.should_receive(:make_api_call).with(::Nexmos.apis[:account][:get_balance], {})
54
+ instance.get_balance
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ context 'instance' do
61
+ subject { ::Nexmos::Base.new('test_api', 'test_secret') }
62
+ context 'new' do
63
+ it 'should raise on empty api_key' do
64
+ expect {::Nexmos::Base.new('', 'test_secret')}.to raise_error('api_key should be set')
65
+ end
66
+ it 'should raise on empty api_secret' do
67
+ expect {::Nexmos::Base.new('test_key', '')}.to raise_error('api_secret should be set')
68
+ end
69
+ it 'should set default_params with values from ::Nexmos module' do
70
+ ::Nexmos.setup do |c|
71
+ c.api_key = 'default_key'
72
+ c.api_secret = 'default_secret'
73
+ end
74
+ instance = ::Nexmos::Base.new
75
+ instance.instance_variable_get('@default_params').should == {'api_key' => 'default_key', 'api_secret' => 'default_secret'}
76
+ end
77
+ it 'should set default_params with custom api key and secret' do
78
+ instance = ::Nexmos::Base.new('test_key', 'test_secret')
79
+ instance.instance_variable_get('@default_params').should == {'api_key' => 'test_key', 'api_secret' => 'test_secret'}
80
+ end
81
+ end
82
+
83
+ its(:connection) { should be_kind_of(::Faraday::Connection) }
84
+
85
+ context 'make_api_call' do
86
+
87
+ before(:each) do
88
+ stub_request(:get, "https://rest.nexmo.com/test/url?api_key=test_api&api_secret=test_secret").
89
+ with(:headers => {'Accept'=>'application/json', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=> ::Nexmos.user_agent}).
90
+ to_return(:status => 200, :body => {'key' => 'value'}, :headers => {})
91
+ end
92
+
93
+ let(:api_params_without_required) do
94
+ {
95
+ :method => :get,
96
+ :url => '/test/url'
97
+ }
98
+ end
99
+
100
+ let(:api_params_with_required) do
101
+ {
102
+ :method => :get,
103
+ :url => '/test/url',
104
+ :required => %w(key1 key2)
105
+ }
106
+ end
107
+
108
+ it 'should call check_required_params' do
109
+ subject.should_receive(:check_required_params)
110
+ subject.make_api_call(api_params_with_required)
111
+ end
112
+
113
+ it 'should raise if all required params missing' do
114
+ expect{subject.make_api_call(api_params_with_required)}.to raise_error('key1,key2 params required')
115
+ end
116
+
117
+ it 'should raise if some required params missing' do
118
+ expect{subject.make_api_call(api_params_with_required,{:key1 => 'val'})}.to raise_error('key2 params required')
119
+ end
120
+
121
+ it 'should call normalize_params' do
122
+ subject.should_receive(:normalize_params).and_call_original
123
+ subject.make_api_call(api_params_without_required)
124
+ end
125
+
126
+ it 'should not call camelize_params' do
127
+ stub_request(:get, "https://rest.nexmo.com/test/url?api_key=test_api&api_secret=test_secret&test_call=value").
128
+ with(webmock_default_headers).
129
+ to_return(:status => 200, :body => {}, :headers => {})
130
+ subject.should_not_receive(:camelize_params)
131
+ subject.make_api_call(api_params_without_required, {'test_call' => 'value'})
132
+ end
133
+
134
+ it 'should call camelize_params' do
135
+ stub_request(:get, "https://rest.nexmo.com/test/url?api_key=test_api&api_secret=test_secret&testCall=value").
136
+ with(webmock_default_headers).
137
+ to_return(:status => 200, :body => {}, :headers => {})
138
+ subject.should_receive(:camelize_params).and_call_original
139
+ subject.make_api_call(api_params_without_required.merge(:camelize => true), {'test_call' => 'value'})
140
+ end
141
+
142
+ it 'should call get_response' do
143
+ stub_request(:get, "https://rest.nexmo.com/test/url?api_key=test_api&api_secret=test_secret").
144
+ with(webmock_default_headers).
145
+ to_return(:status => 200, :body => {}, :headers => {})
146
+ subject.should_receive(:get_response).and_call_original
147
+ subject.make_api_call(api_params_without_required)
148
+ end
149
+
150
+ it 'should return hash' do
151
+ subject.make_api_call(api_params_without_required).should be_a_kind_of(::Hash)
152
+ end
153
+
154
+ it 'should return Hashie::Mash' do
155
+ subject.make_api_call(api_params_without_required).should be_a_kind_of(::Hashie::Mash)
156
+ end
157
+
158
+ it 'should respond to success? method in result' do
159
+ subject.make_api_call(api_params_without_required).should respond_to(:success?)
160
+ end
161
+
162
+ it 'should have success? key in hash' do
163
+ subject.make_api_call(api_params_without_required)[:success?].should be
164
+ end
165
+
166
+ it 'should have success? == true' do
167
+ subject.make_api_call(api_params_without_required)[:success?].should be_true
168
+ end
169
+
170
+ it 'should have success? == false on response with status != 200' do
171
+ stub_request(:get, "https://rest.nexmo.com/test/url?api_key=test_api&api_secret=test_secret&testCall=value").
172
+ with(webmock_default_headers).
173
+ to_return(:status => 410, :body => {}, :headers => {})
174
+ res = subject.make_api_call(api_params_without_required.merge(:camelize => true), {'test_call' => 'value'})
175
+ res[:success?].should be_false
176
+ res[:failed?].should be_false
177
+ res[:not_authorized?].should be_false
178
+ end
179
+
180
+ it 'should have not_authorized? == true' do
181
+ stub_request(:get, "https://rest.nexmo.com/test/url?api_key=test_api&api_secret=test_secret&testCall=value").
182
+ with(webmock_default_headers).
183
+ to_return(:status => 401, :body => {}, :headers => {})
184
+ res = subject.make_api_call(api_params_without_required.merge(:camelize => true), {'test_call' => 'value'})
185
+ res[:success?].should be_false
186
+ res[:not_authorized?].should be_true
187
+ res[:failed?].should be_false
188
+ end
189
+
190
+ it 'should have failed? == true' do
191
+ stub_request(:get, "https://rest.nexmo.com/test/url?api_key=test_api&api_secret=test_secret&testCall=value").
192
+ with(webmock_default_headers).
193
+ to_return(:status => 420, :body => {}, :headers => {})
194
+ res = subject.make_api_call(api_params_without_required.merge(:camelize => true), {'test_call' => 'value'})
195
+ res[:success?].should be_false
196
+ res[:not_authorized?].should be_false
197
+ res[:failed?].should be_true
198
+ end
199
+
200
+ end
201
+
202
+ end
203
+ end
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+ describe ::Nexmos::Number do
3
+ let(:webmock_default_headers) do
4
+ {
5
+ :headers => {
6
+ 'Accept' => 'application/json',
7
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
8
+ 'User-Agent' => ::Nexmos.user_agent
9
+ }
10
+ }
11
+ end
12
+
13
+ let(:finland_numbers_search) do
14
+ {"numbers" => [
15
+ {"type" => "mobile-lvn",
16
+ "msisdn" => "3584573950816",
17
+ "country" => "FI",
18
+ "cost" => "3.00"},
19
+ {"type" => "mobile-lvn",
20
+ "msisdn" => "3584573950815",
21
+ "country" => "FI",
22
+ "cost" => "3.00"},
23
+ {"type" => "mobile-lvn",
24
+ "msisdn" => "3584573950814",
25
+ "country" => "FI",
26
+ "cost" => "3.00"},
27
+ {"type" => "mobile-lvn",
28
+ "msisdn" => "3584573950813",
29
+ "country" => "FI",
30
+ "cost" => "3.00"},
31
+ {"type" => "mobile-lvn",
32
+ "msisdn" => "3584573950812",
33
+ "country" => "FI",
34
+ "cost" => "3.00"},
35
+ {"type" => "mobile-lvn",
36
+ "msisdn" => "3584573950806",
37
+ "country" => "FI",
38
+ "cost" => "3.00"},
39
+ {"type" => "mobile-lvn",
40
+ "msisdn" => "3584573950805",
41
+ "country" => "FI",
42
+ "cost" => "3.00"},
43
+ {"type" => "mobile-lvn",
44
+ "msisdn" => "3584573950804",
45
+ "country" => "FI",
46
+ "cost" => "3.00"},
47
+ {"type" => "mobile-lvn",
48
+ "msisdn" => "3584573950798",
49
+ "country" => "FI",
50
+ "cost" => "3.00"},
51
+ {"type" => "mobile-lvn",
52
+ "msisdn" => "3584573950796",
53
+ "country" => "FI",
54
+ "cost" => "3.00"}],
55
+ "count" => 22}
56
+ end
57
+
58
+ before(:each) do
59
+ ::Nexmos.reset!
60
+ ::Nexmos.setup do |c|
61
+ c.api_key = 'default_key'
62
+ c.api_secret = 'default_secret'
63
+ end
64
+ end
65
+
66
+ subject { ::Nexmos::Number.new }
67
+
68
+ context '#search' do
69
+ it 'should return error on missed param' do
70
+ expect { subject.search }.to raise_error('country params required')
71
+ end
72
+
73
+ it 'should return list of numbers' do
74
+ request = stub_request(:get, "https://rest.nexmo.com/number/search?api_key=default_key&api_secret=default_secret&country=FI").
75
+ with(webmock_default_headers).to_return(:status => 200, :body => finland_numbers_search, :headers => {})
76
+ res = subject.search(:country => 'FI')
77
+ res.should be_kind_of(::Hash)
78
+ res.success?.should be_true
79
+ res['count'].should == 22
80
+ res['numbers'].should be_kind_of(::Array)
81
+ res['numbers'].size.should == 10
82
+ res['numbers'].first.keys.sort.should == ["cost", "country", "msisdn", "type"]
83
+ request.should have_been_made.once
84
+ end
85
+ end
86
+
87
+ context '#buy' do
88
+ it 'should return error on missed params' do
89
+ expect { subject.buy }.to raise_error('country,msisdn params required')
90
+ end
91
+ it 'should return error on missed params' do
92
+ expect { subject.buy :country => 'FI' }.to raise_error('msisdn params required')
93
+ end
94
+
95
+ it 'should buy number' do
96
+ request = stub_request(:post, "https://rest.nexmo.com/number/buy").
97
+ with(
98
+ :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=> ::Nexmos.user_agent},
99
+ :body => {"api_key"=>"default_key", "api_secret"=>"default_secret", "country"=>"FI", "msisdn"=>"3584573950816"}
100
+ ).to_return(:status => 200, :body => "", :headers => {})
101
+ res = subject.buy(:country => 'FI', :msisdn => '3584573950816' )
102
+ res.should be_kind_of(::Hash)
103
+ res.success?.should be_true
104
+ request.should have_been_made.once
105
+ end
106
+
107
+ end
108
+
109
+ context '#cancel' do
110
+
111
+ end
112
+
113
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+ describe ::Nexmos do
3
+ subject{ ::Nexmos }
4
+
5
+ before(:each) do
6
+ subject.reset!
7
+ end
8
+
9
+ context '#reset!' do
10
+ its(:user_agent) { should == "Nexmos v#{::Nexmos::VERSION}" }
11
+ its(:api_key) { should be_nil }
12
+ its(:api_secret) { should be_nil }
13
+ its(:logger) { should be_kind_of(::Logger) }
14
+ end
15
+
16
+ context '#setup' do
17
+
18
+ context 'single call' do
19
+ it 'should set user_agent' do
20
+ subject.setup do |c|
21
+ c.user_agent = 'Test1245'
22
+ end
23
+ subject.user_agent.should == 'Test1245'
24
+ end
25
+
26
+ it 'should set logger' do
27
+ newlogger = ::Logger.new(STDERR)
28
+ subject.setup do |c|
29
+ c.logger = newlogger
30
+ end
31
+ subject.logger.should == newlogger
32
+ end
33
+
34
+ it 'should set api_key' do
35
+ subject.setup do |c|
36
+ c.api_key = 'test-api-key'
37
+ end
38
+ subject.api_key.should == 'test-api-key'
39
+ end
40
+
41
+ it 'should set api_secret' do
42
+ subject.setup do |c|
43
+ c.api_secret = 'test-api-secret'
44
+ end
45
+ subject.api_secret.should == 'test-api-secret'
46
+ end
47
+
48
+ end
49
+
50
+ context 'double call' do
51
+ it 'should not accept running setup more then once' do
52
+ subject.setup do |c|
53
+ c.api_key = 'test-api-key'
54
+ end
55
+ subject.setup do |c|
56
+ c.api_key = 'test-api-key2'
57
+ end
58
+ subject.api_key.should == 'test-api-key'
59
+ end
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,21 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ require 'webmock'
8
+ require 'webmock/rspec'
9
+ require 'nexmos'
10
+
11
+ RSpec.configure do |config|
12
+ config.treat_symbols_as_metadata_keys_with_true_values = true
13
+ config.run_all_when_everything_filtered = true
14
+ config.filter_run :focus
15
+
16
+ # Run specs in random order to surface order dependencies. If you find an
17
+ # order dependency and want to debug it, you can fix the order by providing
18
+ # the seed, which is printed after each run.
19
+ # --seed 1234
20
+ config.order = 'random'
21
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nexmos
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Alexander Simonov
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rash
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: faraday
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: faraday_middleware
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: activesupport
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 3.0.0
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 3.0.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: webmock
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: Nexmo API client
111
+ email:
112
+ - alex@simonov.me
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - .gitignore
118
+ - .rspec
119
+ - .rvmrc
120
+ - .travis.yml
121
+ - Gemfile
122
+ - LICENSE.txt
123
+ - README.md
124
+ - Rakefile
125
+ - lib/api.yml
126
+ - lib/nexmos.rb
127
+ - lib/nexmos/account.rb
128
+ - lib/nexmos/base.rb
129
+ - lib/nexmos/message.rb
130
+ - lib/nexmos/number.rb
131
+ - lib/nexmos/railties.rb
132
+ - lib/nexmos/search.rb
133
+ - lib/nexmos/version.rb
134
+ - nexmos.gemspec
135
+ - spec/nexmos/account_spec.rb
136
+ - spec/nexmos/base_spec.rb
137
+ - spec/nexmos/number_spec.rb
138
+ - spec/nexmos_spec.rb
139
+ - spec/spec_helper.rb
140
+ homepage: https://github.com/simonoff/nexmos
141
+ licenses: []
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ! '>='
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ requirements: []
159
+ rubyforge_project:
160
+ rubygems_version: 1.8.24
161
+ signing_key:
162
+ specification_version: 3
163
+ summary: Nexmo API client
164
+ test_files:
165
+ - spec/nexmos/account_spec.rb
166
+ - spec/nexmos/base_spec.rb
167
+ - spec/nexmos/number_spec.rb
168
+ - spec/nexmos_spec.rb
169
+ - spec/spec_helper.rb