sms77 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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