sms77 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fe2489e8f42e261bf888d5d5d86805caba5e7a293dae1020399069b37ba26e2a
4
+ data.tar.gz: cbb1faa8e91e0c29df20fa27ed31c8baa8a8655582fa180b64d45b24b27545a1
5
+ SHA512:
6
+ metadata.gz: f0db040721b53efa7b02a8dc48af77d7ca6d43f0e5a8f0cdbae8700fef7abd856dd52fa149d68f3e61740a25761b37336274d0f65b3fa7d82071814be65cd824
7
+ data.tar.gz: b22a80577d8b440de469a221f6013bf3fff1daa7ca9f3a8f8df9e9e59f7cfe94aef6ab79bf76c07b0cff800315acd1ac9c91e0b3f1f62a6dd29f01478cb85756
@@ -0,0 +1,4 @@
1
+ /.idea
2
+ sms77.gem
3
+ Gemfile.lock
4
+ .ruby-gemset
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020-present sms77 e.K.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,15 @@
1
+ ![](https://www.sms77.io/wp-content/uploads/2019/07/sms77-Logo-400x79.png "Sms77.io Logo")
2
+
3
+ # Sms77.io SMS Gateway API Client
4
+
5
+ ## Installation
6
+
7
+ ```gem install sms77```
8
+
9
+ ### Usage
10
+
11
+ ```ruby
12
+ client = Sms77::Client.new(api_key: ENV['SMS77_API_KEY'])
13
+ balance = client.get(url: '/api/balance')
14
+ puts balance.inspect
15
+ ```
@@ -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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sms77/version'
4
+ require 'sms77/client'
5
+
6
+ module Sms77
7
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+ require 'json'
5
+ require 'faraday'
6
+ require 'sms77/endpoint'
7
+ require 'sms77/contacts_action'
8
+ require 'sms77/header'
9
+
10
+ module Sms77
11
+ class Client
12
+ def initialize(api_key, conn, sent_with = 'ruby')
13
+ @api_key = api_key
14
+ @conn = conn
15
+ @sent_with = sent_with
16
+
17
+ raise 'missing api_key in config' if !@api_key || @api_key.empty?
18
+ raise 'missing conn in config' unless @conn
19
+
20
+ @conn.headers['sentWith'] = @sent_with
21
+ @conn.authorization :Bearer, @api_key
22
+ end
23
+
24
+ BASE_URI = 'https://gateway.sms77.io'
25
+ API_SUFFIX = '/api/'
26
+ API_URI = "#{BASE_URI}#{API_SUFFIX}"
27
+
28
+ def analytics(params = {})
29
+ get(Sms77::Endpoint::ANALYTICS, params)
30
+ end
31
+
32
+ def balance
33
+ get(Sms77::Endpoint::BALANCE)
34
+ end
35
+
36
+ def contacts(params)
37
+ method(params['action'] == ContactsAction::READ ? 'get' : 'post').call(Sms77::Endpoint::CONTACTS, params)
38
+ end
39
+
40
+ def lookup(params)
41
+ get(Sms77::Endpoint::LOOKUP, params)
42
+ end
43
+
44
+ def pricing(params = {})
45
+ get(Sms77::Endpoint::PRICING, params)
46
+ end
47
+
48
+ def sms(params)
49
+ post(Sms77::Endpoint::SMS, params)
50
+ end
51
+
52
+ def status(params)
53
+ get(Sms77::Endpoint::STATUS, params)
54
+ end
55
+
56
+ def validate_for_voice(params)
57
+ get(Sms77::Endpoint::VALIDATE_FOR_VOICE, params)
58
+ end
59
+
60
+ def voice(params)
61
+ post(Sms77::Endpoint::VOICE, params)
62
+ end
63
+
64
+ private
65
+
66
+ def get(endpoint, params = {})
67
+ request(endpoint, 'get', params)
68
+ end
69
+
70
+ def post(endpoint, params)
71
+ request(endpoint, 'post', params)
72
+ end
73
+
74
+ def request(endpoint, method, params)
75
+ url = "#{API_SUFFIX}#{endpoint}"
76
+
77
+ if ENV['SMS77_DEBUG']
78
+ puts "requesting url: #{url}"
79
+ puts "headers: #{@conn.headers.inspect}"
80
+ end
81
+
82
+ response = if method == 'get'
83
+ @conn.get(url, params)
84
+ else
85
+ @conn.post do |req|
86
+ req.path = url
87
+ req.params = params
88
+ end
89
+ end
90
+
91
+ raise "Error requesting (#{self.class.name}) with code: #{response.status}" if response.status != 200
92
+
93
+ puts "received body: #{response.body}" if ENV['SMS77_DEBUG']
94
+
95
+ response
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sms77
4
+ module ContactsAction
5
+ DEL = 'del'
6
+ READ = 'read'
7
+ WRITE = 'write'
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sms77
4
+ module Endpoint
5
+ ANALYTICS = 'analytics'
6
+ BALANCE = 'balance'
7
+ CONTACTS = 'contacts'
8
+ LOOKUP = 'lookup'
9
+ PRICING = 'pricing'
10
+ SMS = 'sms'
11
+ STATUS = 'status'
12
+ VALIDATE_FOR_VOICE = 'validate_for_voice'
13
+ VOICE = 'voice'
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sms77
4
+ module Header
5
+ CONTENT_TYPE = 'Content-Type'
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sms77
4
+ module LookupType
5
+ CNAM = 'cnam'
6
+ FORMAT = 'format'
7
+ HLR = 'hlr'
8
+ MNP = 'mnp'
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sms77
4
+ VERSION = '0.0.1'
5
+ end
@@ -0,0 +1,25 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'sms77/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'sms77'
8
+ spec.version = Sms77::VERSION.dup
9
+ spec.summary = 'Official Sms77.io API Client for Ruby'
10
+ spec.description = 'Send SMS & Text2Voice messages via the Sms77.io SMS Gateway.'
11
+ spec.authors = ['sms77 e.K.']
12
+ spec.email = ['support@sms77.io']
13
+ spec.homepage = 'https://github.com/sms77io/ruby-client'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 2.6.5'
16
+ spec.require_paths = ['lib']
17
+ spec.files = `git ls-files`.split("\n")
18
+ spec.test_files = Dir['test/**/*']
19
+
20
+ spec.add_runtime_dependency 'faraday', '~> 1'
21
+
22
+ spec.add_development_dependency 'bundler', '~> 2.1'
23
+ spec.add_development_dependency 'rake', '~> 13'
24
+ spec.add_development_dependency 'rspec', '~> 3.0'
25
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'sms77/endpoint'
5
+
6
+ RSpec.describe Sms77, 'balance' do
7
+ it 'returns the account balance' do
8
+ Helper.stubs.get("/api/#{Sms77::Endpoint::BALANCE}") { |_env| [200, {}, '12.34'] } unless Helper.is_http
9
+
10
+ response = Helper.client.balance
11
+
12
+ expect(response.class).to eq(Faraday::Response)
13
+ expect(response.body.to_f).to be_kind_of(Float)
14
+ end
15
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'sms77/endpoint'
5
+ require 'sms77/contacts_action'
6
+
7
+ RSpec.describe Sms77, 'contacts' do
8
+ $new_contact_id = nil
9
+
10
+ def assert_new(response_body)
11
+ if response_body.is_a?(String)
12
+ code, $new_contact_id = response_body.split("\n")
13
+ $new_contact_id = $new_contact_id.to_i
14
+ else
15
+ code = response_body['return']
16
+ $new_contact_id = response_body['id']
17
+ end
18
+
19
+ expect(code).to be_numeric
20
+ expect($new_contact_id).to be_an_instance_of(Integer)
21
+ end
22
+
23
+ def assert_contact(contact)
24
+ if contact.is_a?(String)
25
+ id, name, number = contact.split(';')
26
+
27
+ id = id.gsub('"', '')
28
+ name = name.gsub('"', '')
29
+ number = number.gsub('"', '')
30
+ else
31
+ id = contact['ID']
32
+ name = contact['Name']
33
+ number = contact['Number']
34
+ end
35
+
36
+ expect(id).to be_numeric
37
+ expect(name).to be_an_instance_of(String)
38
+ expect(number.sub('+', '')).to be_numeric
39
+ end
40
+
41
+ def request(action, stub, extra_params = {})
42
+ Helper.request(Sms77::Endpoint::CONTACTS, { action: action }.merge(extra_params), stub)
43
+ end
44
+
45
+ it 'returns all contacts as CSV' do
46
+ stub = <<~CSV
47
+ "4848436";"";""
48
+ "4848437";"";""
49
+ "4848433";"Alf Albert";"007"
50
+ "3172517";"BNN Nolte";"004911112"
51
+ "4848434";"Harry Harald";"0049123456"
52
+ "4848431";"Karl Konrad";"00123456"
53
+ "4848432";"Petra Pan";"00513414"
54
+ "2925186";"Tom Tester";"004901234567890"
55
+ CSV
56
+
57
+ body = request(Sms77::ContactsAction::READ, stub)
58
+
59
+ expect(body).to be_kind_of(String)
60
+
61
+ body.split("\n").each do |contact|
62
+ assert_contact(contact)
63
+ end
64
+ end
65
+
66
+ it 'returns all contacts as CSV' do
67
+ stub = [
68
+ { ID: '4848436', Name: '', Number: '' },
69
+ { ID: '4848437', Name: '', Number: '' },
70
+ { ID: '4848433', Name: 'Alf Albert', Number: '007' },
71
+ { ID: '3172517', Name: 'BNN Nolte', Number: '004911112' },
72
+ { ID: '4848434', Name: 'Harry Harald', Number: '0049123456' },
73
+ { ID: '4848431', Name: 'Karl Konrad', Number: '00123456' },
74
+ { ID: '4848432', Name: 'Petra Pan', Number: '00513414' },
75
+ { ID: '2925186', Name: 'Tom Tester', Number: '004901234567890' }
76
+ ]
77
+
78
+ body = request(Sms77::ContactsAction::READ, stub, { json: 1 })
79
+
80
+ expect(body).to be_kind_of(Array)
81
+
82
+ body.each do |contact|
83
+ assert_contact(contact)
84
+ end
85
+ end
86
+
87
+ it 'creates a contact and returns its ID as TEXT' do
88
+ stub = <<~TEXT
89
+ 152
90
+ 4868400
91
+ TEXT
92
+
93
+ body = request(Sms77::ContactsAction::WRITE, stub)
94
+
95
+ expect(body).to be_kind_of(String)
96
+
97
+ assert_new(body)
98
+ end
99
+
100
+ it 'deletes a contact with given ID and return code' do
101
+ expect(request(Sms77::ContactsAction::DEL, 152, { id: $new_contact_id })).to be_kind_of(Integer)
102
+ end
103
+
104
+ it 'creates a contact and returns its ID as JSON' do
105
+ body = request(Sms77::ContactsAction::WRITE, { id: 4868401, return: '152' }, { json: 1 })
106
+
107
+ expect(body).to be_kind_of(Hash)
108
+
109
+ assert_new(body)
110
+ end
111
+
112
+ it 'deletes a contact with given ID and return code as JSON' do
113
+ body = request(Sms77::ContactsAction::DEL, { return: '152' }, { id: $new_contact_id, json: 1 })
114
+
115
+ expect(body).to be_kind_of(Hash)
116
+ expect(body['return']).to be_kind_of(String)
117
+ end
118
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'logger'
5
+
6
+ describe Sms77::Http do
7
+ before(:all) do
8
+ end
9
+
10
+ after(:all) do
11
+ end
12
+
13
+ after(:each) do
14
+ end
15
+
16
+ let(:config) do
17
+ Helper.config
18
+ end
19
+
20
+ let(:logger) do
21
+ Logger.new($stderr).tap do |logger|
22
+ logger.level = Logger::ERROR
23
+ end
24
+ end
25
+
26
+ let(:instance) do
27
+ described_class.new(config, logger)
28
+ end
29
+
30
+ context 'GET' do
31
+ it 'performs GET requests' do
32
+ Helper.maybe_stub_balance(:get)
33
+
34
+ response = instance.get(url: 'balance')
35
+ puts response.body
36
+
37
+ expect(response.body.to_f).to be_kind_of(Float)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'sms77/endpoint'
5
+ require 'sms77/lookup_type'
6
+ require 'json'
7
+
8
+ RSpec.describe Sms77, 'lookup' do
9
+ def request(type, stub, extra_args = {})
10
+ Helper.stubs.get("/api/#{Sms77::Endpoint::LOOKUP}") { |_env| [200, {}, JSON.generate(stub)] } unless Helper.is_http
11
+
12
+ response = Helper.client.lookup({ type: type, number: '+491771783130' }.merge(extra_args))
13
+
14
+ expect(response.class).to eq(Faraday::Response)
15
+
16
+ body = response.body
17
+
18
+ begin
19
+ body = JSON.parse(body)
20
+ rescue StandardError
21
+ # Ignored
22
+ end
23
+
24
+ body
25
+ end
26
+
27
+ it 'misses number to lookup' do
28
+ stub = {
29
+ carrier: nil,
30
+ country_code: false,
31
+ country_iso: nil,
32
+ country_name: nil,
33
+ international: '+',
34
+ international_formatted: '',
35
+ national: '',
36
+ network_type: nil,
37
+ success: false
38
+ }
39
+
40
+ body = request(Sms77::LookupType::FORMAT, stub, { number: '' })
41
+
42
+ expect(body).to be_kind_of(Hash)
43
+ expect(body['success']).to match(false)
44
+ end
45
+
46
+ it 'returns number formatting details as json' do
47
+ stub = {
48
+ carrier: 'Eplus',
49
+ country_code: '49',
50
+ country_iso: 'DE',
51
+ country_name: 'Germany',
52
+ international: '+491771783130',
53
+ international_formatted: '+49 177 1783130',
54
+ national: '0177 1783130',
55
+ network_type: 'mobile',
56
+ success: true
57
+ }
58
+
59
+ body = request(Sms77::LookupType::FORMAT, stub)
60
+
61
+ expect(body).to be_kind_of(Hash)
62
+ expect(body['carrier']).to be_kind_of(String)
63
+ expect(body['country_code']).to be_kind_of(String)
64
+ expect(body['country_iso']).to be_kind_of(String)
65
+ expect(body['country_name']).to be_kind_of(String)
66
+ expect(body['international']).to be_kind_of(String)
67
+ expect(body['international_formatted']).to be_kind_of(String)
68
+ expect(body['national']).to be_kind_of(String)
69
+ expect(body['network_type']).to be_kind_of(String)
70
+ expect(body['success']).to be_boolean
71
+ end
72
+
73
+ it 'returns CNAM details as json' do
74
+ stub = {
75
+ code: '100',
76
+ name: 'GERMANY',
77
+ number: '+491771783130',
78
+ success: 'true'
79
+ }
80
+ body = request(Sms77::LookupType::CNAM, stub)
81
+
82
+ expect(body).to be_kind_of(Hash)
83
+ expect(body['code']).to be_kind_of(String)
84
+ expect(body['name']).to be_kind_of(String)
85
+ expect(body['number']).to be_kind_of(String)
86
+ expect(body['success']).to be_kind_of(String)
87
+ end
88
+
89
+ it 'returns MNP details as text' do
90
+ body = request(Sms77::LookupType::MNP, 'eplus')
91
+
92
+ expect(body).to be_kind_of(String)
93
+ end
94
+
95
+ it 'returns MNP details as json' do
96
+ stub = {
97
+ code: 100,
98
+ mnp: {
99
+ country: 'DE',
100
+ international_formatted: '+49 177 1783130',
101
+ isPorted: false,
102
+ mccmnc: '26203',
103
+ national_format: '0177 1783130',
104
+ network: "Telef\u00f3nica Germany GmbH & Co. oHG (O2)",
105
+ number: '+491771783130'
106
+ },
107
+ price: 0.005,
108
+ success: true
109
+ }
110
+ body = request(Sms77::LookupType::MNP, stub, { json: 1 })
111
+
112
+ expect(body).to be_kind_of(Hash)
113
+ expect(body['code']).to be_kind_of(Integer)
114
+ expect(body['price']).to be_kind_of(Float)
115
+ expect(body['mnp']).to be_kind_of(Hash)
116
+ expect(body['mnp']['country']).to be_kind_of(String)
117
+ expect(body['mnp']['international_formatted']).to be_kind_of(String)
118
+ expect(body['mnp']['isPorted']).to be_boolean
119
+ expect(body['mnp']['mccmnc']).to be_kind_of(String)
120
+ expect(body['mnp']['national_format']).to be_kind_of(String)
121
+ expect(body['mnp']['network']).to be_kind_of(String)
122
+ expect(body['mnp']['number']).to be_kind_of(String)
123
+ expect(body['success']).to be_boolean
124
+ end
125
+
126
+ it 'returns HLR details as json' do
127
+ stub = {
128
+ country_code: 'DE',
129
+ country_name: 'Germany',
130
+ country_prefix: '49',
131
+ current_carrier: {
132
+ country: 'DE',
133
+ name: "Telef\u00f3nica Germany GmbH & Co. oHG (O2)",
134
+ network_code: '26203',
135
+ network_type: 'mobile'
136
+ },
137
+ international_format_number: '491771783130',
138
+ international_formatted: '+49 177 1783130',
139
+ lookup_outcome: true,
140
+ lookup_outcome_message: 'success',
141
+ national_format_number: '0177 1783130',
142
+ original_carrier: {
143
+ country: 'DE',
144
+ name: "Telef\u00f3nica Germany GmbH & Co. oHG (O2)",
145
+ network_code: '26203',
146
+ network_type: 'mobile'
147
+ },
148
+ status: true,
149
+ status_message: 'success',
150
+ gsm_code: '0',
151
+ gsm_message: 'No error',
152
+ ported: 'assumed_not_ported',
153
+ reachable: 'reachable',
154
+ roaming: 'not_roaming',
155
+ valid_number: 'valid'
156
+ }
157
+
158
+ body = request(Sms77::LookupType::HLR, stub)
159
+
160
+ expect(body).to be_kind_of(Hash)
161
+ expect(body['country_code']).to be_kind_of(String)
162
+ expect(body['country_name']).to be_kind_of(String)
163
+ expect(body['country_prefix']).to be_kind_of(String)
164
+ expect(body['current_carrier']).to be_kind_of(Hash)
165
+ expect(body['current_carrier']['country']).to be_kind_of(String)
166
+ expect(body['current_carrier']['name']).to be_kind_of(String)
167
+ expect(body['current_carrier']['network_code']).to be_kind_of(String)
168
+ expect(body['current_carrier']['network_type']).to be_kind_of(String)
169
+ expect(body['international_format_number']).to be_kind_of(String)
170
+ expect(body['international_formatted']).to be_kind_of(String)
171
+ expect(body['lookup_outcome']).to be_boolean
172
+ expect(body['lookup_outcome_message']).to be_kind_of(String)
173
+ expect(body['national_format_number']).to be_kind_of(String)
174
+ expect(body['original_carrier']).to be_kind_of(Hash)
175
+ expect(body['original_carrier']['country']).to be_kind_of(String)
176
+ expect(body['original_carrier']['name']).to be_kind_of(String)
177
+ expect(body['original_carrier']['network_code']).to be_kind_of(String)
178
+ expect(body['original_carrier']['network_type']).to be_kind_of(String)
179
+ expect(body['status']).to be_boolean
180
+ expect(body['status_message']).to be_kind_of(String)
181
+ expect(body['gsm_code']).to be_kind_of(String)
182
+ expect(body['gsm_message']).to be_kind_of(String)
183
+ expect(body['ported']).to be_kind_of(String)
184
+ expect(body['reachable']).to be_kind_of(String)
185
+ expect(body['roaming']).to be_kind_of(String)
186
+ expect(body['valid_number']).to be_kind_of(String)
187
+ end
188
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'sms77/endpoint'
5
+ require 'json'
6
+
7
+ RSpec.describe Sms77, 'pricing' do
8
+ it 'returns all countries pricing as json' do
9
+ stub = {
10
+ countCountries: 1,
11
+ countNetworks: 4,
12
+ countries: [
13
+ {
14
+ countryCode: 'AT',
15
+ countryName: 'Austria',
16
+ countryPrefix: '43',
17
+ networks: [
18
+ {
19
+ comment: '',
20
+ features: [],
21
+ mcc: '232',
22
+ mncs: ['02'],
23
+ networkName: 'A1 Telekom Austria',
24
+ price: 0.075
25
+ },
26
+ {
27
+ comment: '',
28
+ features: %w[alpha Numeric dlr sc],
29
+ mcc: '232',
30
+ mncs: ['01'],
31
+ networkName: 'A1 Telekom Austria (A1.net)',
32
+ price: 0.075
33
+ }
34
+ ]
35
+ }
36
+ ]
37
+ }
38
+
39
+ Helper.stubs.get("/api/#{Sms77::Endpoint::PRICING}") { |_env| [200, {}, JSON.generate(stub)] } unless Helper.is_http
40
+
41
+ response = Helper.client.pricing
42
+ body = JSON.parse(response.body)
43
+ countries = body['countries']
44
+
45
+ expect(response.class).to eq(Faraday::Response)
46
+ expect(body).to be_kind_of(Hash)
47
+ expect(body['countCountries']).to be_kind_of(Integer)
48
+ expect(body['countNetworks']).to be_kind_of(Integer)
49
+ expect(countries).to be_kind_of(Array)
50
+
51
+ countries.each do |country|
52
+ networks = country['networks']
53
+
54
+ expect(country).to be_kind_of(Hash)
55
+ expect(country['countryCode']).to be_kind_of(String)
56
+ expect(country['countryName']).to be_kind_of(String)
57
+ expect(country['countryPrefix']).to be_kind_of(String)
58
+ expect(networks).to be_kind_of(Array)
59
+
60
+ networks.each do |network|
61
+ mncs = network['mncs']
62
+ features = network['features']
63
+
64
+ expect(network).to be_kind_of(Hash)
65
+ expect(network['mcc']).to be_kind_of(String)
66
+ expect(mncs).to be_kind_of(Array)
67
+ mncs.each do |mnc|
68
+ expect(mnc).to be_kind_of(String)
69
+ end
70
+ expect(network['networkName']).to be_kind_of(String)
71
+ expect(network['price']).to be_kind_of(Float)
72
+ expect(features).to be_kind_of(Array)
73
+ features.each do |feature|
74
+ expect(feature).to be_kind_of(String)
75
+ end
76
+ expect(network['comment']).to be_kind_of(String)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'sms77/endpoint'
5
+ require 'sms77/contacts_action'
6
+
7
+ RSpec.describe Sms77, 'sms' do
8
+ $text = 'Your glasses are ready for pickup.'
9
+
10
+ def assert_response(response)
11
+ if response.is_a?(String)
12
+ id, name, number = response.split("\n")
13
+ else
14
+ id = response['ID']
15
+ name = response['Name']
16
+ number = response['Number']
17
+ end
18
+
19
+ expect(Integer(id)).to be_an_instance_of(Integer)
20
+ expect(name.to_f).to be_an_instance_of(Float) if id != ''
21
+ expect(number.to_f).to be_an_instance_of(Float)
22
+ end
23
+
24
+ def request(stub, extra_params = {})
25
+ params = {
26
+ from: Helper.virtual_inbound_nr_eplus,
27
+ text: $text,
28
+ to: Helper.virtual_inbound_nr_eplus
29
+ }.merge(extra_params)
30
+
31
+ Helper.request(Sms77::Endpoint::SMS, params, stub)
32
+ end
33
+
34
+ it 'sends a single sms and returns success code' do
35
+ expect(request(100)).to be_kind_of(Integer)
36
+ end
37
+
38
+ it 'sends a single sms and returns detailed text response' do
39
+ stub = <<~TEXT
40
+ 100
41
+ Verbucht: 0
42
+ Preis: 0.075
43
+ Guthaben: 4.575
44
+ Text: Your glasses are ready for pickup.
45
+ SMS-Typ: direct
46
+ Flash SMS: false
47
+ Encoding: gsm
48
+ GSM0338: true
49
+ Debug: true
50
+ TEXT
51
+
52
+ body = request(stub, { details: 1 })
53
+
54
+ expect(body).to be_kind_of(String)
55
+
56
+ code, booked, cost, balance, text, type, flash, encoding, gsm0338, debug = body.split("\n")
57
+
58
+ expect(code).to be_kind_of(String)
59
+ expect(booked.split(':').last.to_f).to be_kind_of(Float)
60
+ expect(cost.split(':').last.to_f).to be_kind_of(Float)
61
+ expect(balance.split(':').last.to_f).to be_kind_of(Float)
62
+ expect(text.split(':').last.strip!).to eq($text)
63
+ expect(type.split(':').last.strip!).to eq('direct')
64
+ expect(flash.split(':').last.strip!).to eq('false')
65
+ expect(encoding.split(':').last.strip!).to eq('gsm')
66
+ expect(gsm0338.split(':').last.strip!).to eq('true')
67
+ expect(debug.split(':').last.strip!).to eq('true')
68
+ end
69
+
70
+ it 'sends a single sms and returns json response' do
71
+ stub = {
72
+ success: '100',
73
+ total_price: 0,
74
+ balance: 4.5,
75
+ debug: 'true',
76
+ sms_type: 'direct',
77
+ messages: [{ id: nil,
78
+ sender: '491771783130',
79
+ recipient: '491771783130',
80
+ text: 'Your glasses are ready for pickup.',
81
+ encoding: 'gsm',
82
+ parts: 1,
83
+ price: 0,
84
+ success: true,
85
+ error: nil,
86
+ error_text: nil }]
87
+ }
88
+
89
+ body = request(stub, { json: 1 })
90
+
91
+ expect(body).to be_kind_of(Hash)
92
+
93
+ expect(body['success']).to be_kind_of(String)
94
+ expect(body['total_price']).to be_numeric
95
+ expect(body['balance']).to be_kind_of(Float)
96
+ expect(body['debug']).to be_kind_of(String)
97
+ expect(body['sms_type']).to eq('direct')
98
+ expect(body['messages']).to be_kind_of(Array)
99
+ body['messages'].each do |message|
100
+ expect(message).to be_kind_of(Hash)
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'sms77/endpoint'
5
+ require 'json'
6
+
7
+ RSpec.describe Sms77, 'validate_for_voice' do
8
+ it 'returns caller id information' do
9
+ number = '491771783130'
10
+ callback_host = Helper.is_http ? `curl http://ipecho.net/plain` : '127.0.0.1'
11
+ callback = "#{callback_host}/callback.php"
12
+ stub = { success: true }
13
+
14
+ body = Helper.request(Sms77::Endpoint::VALIDATE_FOR_VOICE, { number: number, callback: callback }, stub)
15
+
16
+ expect(body).to be_kind_of(Hash)
17
+ expect(body['success']).to be_boolean
18
+ end
19
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'sms77/endpoint'
5
+ require 'sms77/contacts_action'
6
+
7
+ RSpec.describe Sms77, 'voice' do
8
+ def assert_response(response)
9
+ expect(response).to be_kind_of(String)
10
+
11
+ code, id, cost = response.split("\n")
12
+
13
+ expect(Integer(code)).to be_an_instance_of(Integer)
14
+ expect(id.to_f).to be_an_instance_of(Float) if id != ''
15
+ expect(cost.to_f).to be_an_instance_of(Float)
16
+ end
17
+
18
+ def request(text, extra_params = {})
19
+ stub = <<~TEXT
20
+ 301
21
+
22
+ 0.1
23
+ TEXT
24
+
25
+ params = { to: Helper.virtual_inbound_nr_eplus, text: text, from: Helper.virtual_inbound_nr_eplus }
26
+ .merge(extra_params)
27
+
28
+ Helper.request(Sms77::Endpoint::VOICE, params, stub)
29
+ end
30
+
31
+ it 'calls a number with text input' do
32
+ assert_response(request('Your glasses are ready for pickup.'))
33
+ end
34
+
35
+ it 'calls a number with xml input' do
36
+ text = <<~XML
37
+ <?xml version="1.0" encoding="UTF-8"?>
38
+ <Response>
39
+ <Say voice="woman" language="en-EN">
40
+ Your glasses are ready for pickup.
41
+ </Say>
42
+ <Record maxlength="20" />
43
+ </Response>
44
+ XML
45
+
46
+ assert_response(request(text, { xml: 1 }))
47
+ end
48
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sms77 do
6
+ it 'has a version number' do
7
+ expect(Sms77::VERSION).not_to be nil
8
+ end
9
+
10
+ context 'failing authentication' do
11
+ it 'balance' do
12
+ expect { Sms77::Client.new('NotExistingApiKey', nil) }.to raise_error(RuntimeError)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
4
+
5
+ require 'sms77'
6
+
7
+ RSpec::Matchers.define :be_boolean do
8
+ match do |value|
9
+ [true, false].include? value
10
+ end
11
+ end
12
+
13
+ RSpec::Matchers.define :be_numeric do
14
+ match do |value|
15
+ return true if value.is_a?(Integer)
16
+
17
+ value.scan(/\D/).empty?
18
+ end
19
+ end
20
+
21
+ class Helper
22
+ @is_http = ENV.key?('TEST_HTTP')
23
+ @stubs = Faraday::Adapter::Test::Stubs.new
24
+ @conn = Faraday.new(Sms77::Client::BASE_URI) do |b|
25
+ b.adapter(:test, @stubs) unless @is_http
26
+ end
27
+ @client = Sms77::Client.new(ENV['SMS77_DUMMY_API_KEY'], @conn, 'ruby-test')
28
+ @virtual_inbound_nr_eplus = '+491771783130'
29
+
30
+ def self.request(endpoint, params, stub, extra_params = {})
31
+ path = "#{Sms77::Client::API_SUFFIX}#{endpoint}"
32
+
33
+ # TODO-START: determine request method to avoid multiple useless stubs
34
+ @stubs.get(path) { |_env| [200, {}, JSON.generate(stub)] } unless @is_http
35
+ @stubs.post(path) { |_env| [200, {}, JSON.generate(stub)] } unless @is_http
36
+ # TODO-END
37
+
38
+ response = @client.method(endpoint).call(params.merge(extra_params))
39
+
40
+ raise 'unexpected response' unless Faraday::Response == response.class
41
+
42
+ body = response.body
43
+
44
+ begin
45
+ body = JSON.parse(body)
46
+ rescue StandardError
47
+ # Ignored
48
+ end
49
+
50
+ body
51
+ end
52
+
53
+ class << self
54
+ attr_reader :is_http, :stubs, :conn, :client, :virtual_inbound_nr_eplus
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sms77
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - sms77 e.K.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-10-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: Send SMS & Text2Voice messages via the Sms77.io SMS Gateway.
70
+ email:
71
+ - support@sms77.io
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - Gemfile
78
+ - LICENSE
79
+ - README.md
80
+ - Rakefile
81
+ - lib/sms77.rb
82
+ - lib/sms77/client.rb
83
+ - lib/sms77/contacts_action.rb
84
+ - lib/sms77/endpoint.rb
85
+ - lib/sms77/header.rb
86
+ - lib/sms77/lookup_type.rb
87
+ - lib/sms77/version.rb
88
+ - sms77.gemspec
89
+ - spec/sms77/balance_spec.rb
90
+ - spec/sms77/contacts_spec.rb
91
+ - spec/sms77/http_spec.rb
92
+ - spec/sms77/lookup_spec.rb
93
+ - spec/sms77/pricing_spec.rb
94
+ - spec/sms77/sms_spec.rb
95
+ - spec/sms77/validate_for_voice_spec.rb
96
+ - spec/sms77/voice_spec.rb
97
+ - spec/sms77_spec.rb
98
+ - spec/spec_helper.rb
99
+ homepage: https://github.com/sms77io/ruby-client
100
+ licenses:
101
+ - MIT
102
+ metadata: {}
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: 2.6.5
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubygems_version: 3.1.2
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Official Sms77.io API Client for Ruby
122
+ test_files: []