sms77 0.0.2 → 0.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3620fb062cfa005c5da38bc6322048a6525d7aeb25b9f1be3f2c5995fbfc5f3a
4
- data.tar.gz: 7411562537013fd906485c59278e03541c558f96f7207e1e931335952be1530b
3
+ metadata.gz: 621b698c5ea5c6f08f25ebb60d427a949fe8a011de54e82623234d4af10e0c7f
4
+ data.tar.gz: 11e68e54c358e93aacb25e6da5289d2fe904b8def5ef86ce9cdd2b1aac68ae35
5
5
  SHA512:
6
- metadata.gz: 340cfd8ccb98a99d7458efd11d2172e31330147c515e63af019af9c0a5f731c92dd983673accbd1083df1547720bba9a4c258a91df074bd89f390a42141dc95b
7
- data.tar.gz: 205dad2eb5dd22f48982077dfd6f3dc9849cf31bdd721fc747f07199c2468f2c04543bf7f60184d46bae70c63d1fa9726998114d75e22c511f5624e9d067b7c6
6
+ metadata.gz: b86d9e8884dad820fc01892f002c5e90de2162a40dd326c8c95c2cda8a404304924aeb02fc460f79f25aa78089d703662530a5e2995060ff2c8d3c9d4bfb6891
7
+ data.tar.gz: b3650f5844d714e1d925aaf31c8fd8e2b73b49c955220096654044239ef0fa978e3b00a3ae6ced2f8b9818825e8da3aa80ab908f79945204112add325abf78b2
data/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  ```ruby
12
12
  require 'sms77'
13
13
 
14
- client = Sms77::Client.new(ENV['SMS77_API_KEY'], Faraday.new(Sms77::Client::BASE_URI))
14
+ client = Sms77::Client.new(ENV['SMS77_API_KEY'])
15
15
 
16
- puts "Balance: #{client.balance.body}"
16
+ puts "Balance: #{client.balance}"
17
17
  ```
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+ require 'json'
5
+ require 'faraday'
6
+ require 'sms77/endpoint'
7
+
8
+ module Sms77
9
+ class Base
10
+ BASE_PATH = '/api/'
11
+ CONN = Faraday.new("https://gateway.sms77.io#{BASE_PATH}")
12
+ HTTP_GET = CONN.method(:get).freeze
13
+ HTTP_POST = CONN.method(:post).freeze
14
+ CONN.freeze
15
+ HTTP_METHODS = [HTTP_GET, HTTP_POST].freeze
16
+ BUILDER = CONN.builder
17
+
18
+ def initialize(api_key, sent_with = 'ruby')
19
+ raise 'missing api_key in config' if api_key.to_s.empty?
20
+ raise 'missing sent_with in config' if sent_with.to_s.empty?
21
+
22
+ @api_key = api_key
23
+ @sent_with = sent_with
24
+
25
+ HTTP_METHODS.each do |method|
26
+ define_singleton_method(method.name) { |*args| request(method, *args) }
27
+ end
28
+ end
29
+
30
+ attr_reader :api_key, :sent_with
31
+
32
+ protected
33
+
34
+ def request(method, path, payload = {})
35
+ if !payload.empty? && HTTP_GET == method
36
+ path = "#{path}?#{URI.encode_www_form(payload)}"
37
+
38
+ payload = {}
39
+ end
40
+
41
+ method = method.name
42
+ headers = Hash[
43
+ Faraday::Request::Authorization::KEY, "Bearer #{@api_key}",
44
+ 'sentWith', @sent_with
45
+ ]
46
+
47
+ res = CONN.run_request(method, path, payload, headers)
48
+
49
+ puts JSON.pretty_generate(res.to_hash.merge({
50
+ :method => method,
51
+ :path => path,
52
+ :payload => payload,
53
+ :req_headers => headers
54
+ }).compact) if ENV['SMS77_DEBUG']
55
+
56
+ raise "Error requesting (#{self.class.name}) with code #{res.status}" unless 200 == res.status
57
+
58
+ raise 'Unexpected response' unless res.is_a?(Faraday::Response)
59
+
60
+ body = res.body
61
+
62
+ if body.is_a?(String)
63
+ begin
64
+ body = JSON.parse(body, :symbolize_names => true)
65
+ rescue StandardError
66
+ # Ignored
67
+ end
68
+ end
69
+
70
+ body.map! { |hash| hash.transform_keys(&:to_sym) } if body.is_a?(Array)
71
+
72
+ body
73
+ end
74
+ end
75
+ end
@@ -4,27 +4,11 @@ require 'cgi'
4
4
  require 'json'
5
5
  require 'faraday'
6
6
  require 'sms77/endpoint'
7
- require 'sms77/contacts_action'
8
- require 'sms77/header'
7
+ require 'sms77/contacts'
8
+ require 'sms77/base'
9
9
 
10
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
-
11
+ class Client < Sms77::Base
28
12
  def analytics(params = {})
29
13
  get(Sms77::Endpoint::ANALYTICS, params)
30
14
  end
@@ -34,11 +18,17 @@ module Sms77
34
18
  end
35
19
 
36
20
  def contacts(params)
37
- method(params['action'] == ContactsAction::READ ? 'get' : 'post').call(Sms77::Endpoint::CONTACTS, params)
21
+ get_or_post(Sms77::Contacts::Action::READ == params[:action], Sms77::Endpoint::CONTACTS, params)
22
+ end
23
+
24
+ def hooks(params)
25
+ Sms77::Hooks::Validator::validate(params)
26
+
27
+ get_or_post(Sms77::Hooks::Action::READ == params[:action], Sms77::Endpoint::HOOKS, params)
38
28
  end
39
29
 
40
30
  def lookup(params)
41
- get(Sms77::Endpoint::LOOKUP, params)
31
+ post(Sms77::Endpoint::LOOKUP, params)
42
32
  end
43
33
 
44
34
  def pricing(params = {})
@@ -54,7 +44,7 @@ module Sms77
54
44
  end
55
45
 
56
46
  def validate_for_voice(params)
57
- get(Sms77::Endpoint::VALIDATE_FOR_VOICE, params)
47
+ post(Sms77::Endpoint::VALIDATE_FOR_VOICE, params)
58
48
  end
59
49
 
60
50
  def voice(params)
@@ -63,36 +53,8 @@ module Sms77
63
53
 
64
54
  private
65
55
 
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
56
+ def get_or_post(bool, *args)
57
+ method(bool ? :get : :post).call(*args)
96
58
  end
97
59
  end
98
60
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Sms77
4
- module ContactsAction
3
+ module Sms77::Contacts
4
+ module Action
5
5
  DEL = 'del'
6
6
  READ = 'read'
7
7
  WRITE = 'write'
@@ -1,15 +1,14 @@
1
1
  # frozen_string_literal: true
2
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
3
+ module Sms77::Endpoint
4
+ ANALYTICS = 'analytics'
5
+ BALANCE = 'balance'
6
+ CONTACTS = 'contacts'
7
+ HOOKS = 'hooks'
8
+ LOOKUP = 'lookup'
9
+ PRICING = 'pricing'
10
+ SMS = 'sms'
11
+ STATUS = 'status'
12
+ VALIDATE_FOR_VOICE = 'validate_for_voice'
13
+ VOICE = 'voice'
15
14
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sms77::Hooks
4
+ module Action
5
+ READ = 'read'
6
+ SUBSCRIBE = 'subscribe'
7
+ UNSUBSCRIBE = 'unsubscribe'
8
+ end
9
+
10
+ module EventType
11
+ NEW_INBOUND_SMS = 'sms_mo'
12
+ SMS_STATUS_UPDATE = 'dlr'
13
+ VOICE_STATUS_UPDATE = 'voice_status'
14
+ end
15
+
16
+ module RequestMethod
17
+ GET = 'GET'
18
+ POST = 'POST'
19
+ end
20
+
21
+ module Validator
22
+ def self.validate(params)
23
+ action = params[:action]
24
+
25
+ raise "Unknown action #{action}" unless Sms77::Hooks::Validator::is_action?(action)
26
+
27
+ if Sms77::Hooks::Action::SUBSCRIBE == action
28
+ raise 'Parameter validation failed' unless Sms77::Hooks::Validator::subscribe(params)
29
+ elsif Sms77::Hooks::Action::UNSUBSCRIBE == action
30
+ raise 'ID must be a positive integer' unless Sms77::Util::is_positive_integer?(params[:id])
31
+ end
32
+ end
33
+
34
+ def self.subscribe(params)
35
+ { :request_method => Sms77::Hooks::RequestMethod::POST }.merge!(params)
36
+
37
+ self.event_type?(params[:event_type]) &&
38
+ self.request_method?(params[:request_method]) &&
39
+ self.target_url?(params[:target_url])
40
+ end
41
+
42
+ def self.is_action?(str)
43
+ Sms77::Util::in_module_constants?(str, Sms77::Hooks::Action)
44
+ end
45
+
46
+ def self.event_type?(str)
47
+ Sms77::Util::in_module_constants?(str, Sms77::Hooks::EventType)
48
+ end
49
+
50
+ def self.request_method?(str)
51
+ Sms77::Util::in_module_constants?(str, Sms77::Hooks::RequestMethod)
52
+ end
53
+
54
+ def self.target_url?(str)
55
+ Sms77::Util::is_valid_url?(str)
56
+ end
57
+ end
58
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Sms77
4
- module LookupType
3
+ module Sms77::Lookup
4
+ module Type
5
5
  CNAM = 'cnam'
6
6
  FORMAT = 'format'
7
7
  HLR = 'hlr'
@@ -0,0 +1,28 @@
1
+ require 'date'
2
+
3
+ module Sms77::Util
4
+ def self.get_module_constant_values(mod)
5
+ mod.constants(false).map &mod.method(:const_get)
6
+ end
7
+
8
+ def self.is_valid_url?(str)
9
+ str =~ URI::regexp
10
+ end
11
+
12
+ def self.is_valid_datetime?(str)
13
+ begin
14
+ DateTime.parse(str)
15
+ true
16
+ rescue ArgumentError
17
+ false
18
+ end
19
+ end
20
+
21
+ def self.is_positive_integer?(val)
22
+ /\A\d+\z/.match?(val.to_s)
23
+ end
24
+
25
+ def self.in_module_constants?(needle, mod)
26
+ get_module_constant_values(mod).include?(needle)
27
+ end
28
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sms77
4
- VERSION = '0.0.2'
4
+ VERSION = '0.1.0'
5
5
  end
@@ -4,22 +4,19 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'sms77/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = 'sms77'
8
- spec.version = Sms77::VERSION.dup
9
- spec.summary = 'Official API Client for the Sms77.io SMS Gateway'
7
+ spec.add_development_dependency 'bundler', '~> 2.1'
8
+ spec.add_development_dependency 'rake', '~> 13.0'
9
+ spec.add_development_dependency 'rspec', '~> 3.9'
10
+ spec.add_runtime_dependency 'faraday', '~> 1.1'
11
+ spec.author = 'sms77 e.K.'
10
12
  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.email = 'support@sms77.io'
14
+ spec.files = `git ls-files`.split("\n")
13
15
  spec.homepage = 'https://github.com/sms77io/ruby-client'
14
16
  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'
17
+ spec.name = 'sms77'
18
+ spec.required_ruby_version = '>= 2.6.0'
19
+ spec.summary = 'Official API Client for the Sms77.io SMS Gateway'
20
+ spec.test_files = Dir['spec/**/*']
21
+ spec.version = Sms77::VERSION
25
22
  end
@@ -5,11 +5,6 @@ require 'sms77/endpoint'
5
5
 
6
6
  RSpec.describe Sms77, 'balance' do
7
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)
8
+ expect(Helper.get(Sms77::Endpoint::BALANCE, 12.34)).to be_a(Float)
14
9
  end
15
10
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'sms77/endpoint'
5
+
6
+ RSpec.describe Sms77, 'client' do
7
+ it 'checks api key' do
8
+ expect(Helper.client.api_key).to be_lengthy(String)
9
+ end
10
+
11
+ it 'checks sentWith' do
12
+ expect(Helper.client.sent_with).to be_lengthy(String)
13
+ end
14
+
15
+ it 'fails authentication' do
16
+ expect { Sms77::Client.new('') }.to raise_error(RuntimeError)
17
+ end
18
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
  require 'sms77/endpoint'
5
- require 'sms77/contacts_action'
5
+ require 'sms77/contacts'
6
6
 
7
7
  RSpec.describe Sms77, 'contacts' do
8
8
  $new_contact_id = nil
@@ -12,8 +12,8 @@ RSpec.describe Sms77, 'contacts' do
12
12
  code, $new_contact_id = response_body.split("\n")
13
13
  $new_contact_id = $new_contact_id.to_i
14
14
  else
15
- code = response_body['return']
16
- $new_contact_id = response_body['id']
15
+ code = response_body[:return]
16
+ $new_contact_id = response_body[:id]
17
17
  end
18
18
 
19
19
  expect(code).to be_numeric
@@ -28,9 +28,9 @@ RSpec.describe Sms77, 'contacts' do
28
28
  name = name.gsub('"', '')
29
29
  number = number.gsub('"', '')
30
30
  else
31
- id = contact['ID']
32
- name = contact['Name']
33
- number = contact['Number']
31
+ id = contact[:ID]
32
+ name = contact[:Name]
33
+ number = contact[:Number]
34
34
  end
35
35
 
36
36
  expect(id).to be_numeric
@@ -39,7 +39,8 @@ RSpec.describe Sms77, 'contacts' do
39
39
  end
40
40
 
41
41
  def request(action, stub, extra_params = {})
42
- Helper.request(Sms77::Endpoint::CONTACTS, { action: action }.merge(extra_params), stub)
42
+ Helper.method(Sms77::Contacts::Action::READ == action ? :get : :post)
43
+ .call(Sms77::Endpoint::CONTACTS, stub, { action: action }.merge(extra_params))
43
44
  end
44
45
 
45
46
  it 'returns all contacts as CSV' do
@@ -54,16 +55,16 @@ RSpec.describe Sms77, 'contacts' do
54
55
  "2925186";"Tom Tester";"004901234567890"
55
56
  CSV
56
57
 
57
- body = request(Sms77::ContactsAction::READ, stub)
58
+ body = request(Sms77::Contacts::Action::READ, stub)
58
59
 
59
- expect(body).to be_kind_of(String)
60
+ expect(body).to be_a(String)
60
61
 
61
62
  body.split("\n").each do |contact|
62
63
  assert_contact(contact)
63
64
  end
64
65
  end
65
66
 
66
- it 'returns all contacts as CSV' do
67
+ it 'returns all contacts as JSON' do
67
68
  stub = [
68
69
  { ID: '4848436', Name: '', Number: '' },
69
70
  { ID: '4848437', Name: '', Number: '' },
@@ -75,9 +76,9 @@ RSpec.describe Sms77, 'contacts' do
75
76
  { ID: '2925186', Name: 'Tom Tester', Number: '004901234567890' }
76
77
  ]
77
78
 
78
- body = request(Sms77::ContactsAction::READ, stub, { json: 1 })
79
+ body = request(Sms77::Contacts::Action::READ, stub, { json: 1 })
79
80
 
80
- expect(body).to be_kind_of(Array)
81
+ expect(body).to be_a(Array)
81
82
 
82
83
  body.each do |contact|
83
84
  assert_contact(contact)
@@ -90,29 +91,29 @@ RSpec.describe Sms77, 'contacts' do
90
91
  4868400
91
92
  TEXT
92
93
 
93
- body = request(Sms77::ContactsAction::WRITE, stub)
94
+ body = request(Sms77::Contacts::Action::WRITE, stub)
94
95
 
95
- expect(body).to be_kind_of(String)
96
+ expect(body).to be_a(String)
96
97
 
97
98
  assert_new(body)
98
99
  end
99
100
 
100
101
  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
+ expect(request(Sms77::Contacts::Action::DEL, 152, { id: $new_contact_id })).to be_a(Integer)
102
103
  end
103
104
 
104
105
  it 'creates a contact and returns its ID as JSON' do
105
- body = request(Sms77::ContactsAction::WRITE, { id: 4868401, return: '152' }, { json: 1 })
106
+ body = request(Sms77::Contacts::Action::WRITE, { id: 4868401, return: '152' }, { json: 1 })
106
107
 
107
- expect(body).to be_kind_of(Hash)
108
+ expect(body).to be_a(Hash)
108
109
 
109
110
  assert_new(body)
110
111
  end
111
112
 
112
113
  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
+ body = request(Sms77::Contacts::Action::DEL, { return: '152' }, { id: $new_contact_id, json: 1 })
114
115
 
115
- expect(body).to be_kind_of(Hash)
116
- expect(body['return']).to be_kind_of(String)
116
+ expect(body).to be_a(Hash)
117
+ expect(body[:return]).to be_a(String)
117
118
  end
118
119
  end