sms77 0.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,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: []