sms_candy 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +34 -0
- data/lib/sms_candy.rb +16 -0
- data/lib/sms_candy/authentication/strategies.rb +39 -0
- data/lib/sms_candy/authentication/strategies/o_auth_2.rb +44 -0
- data/lib/sms_candy/config.rb +16 -0
- data/lib/sms_candy/helpers/get_message_response_helper.rb +40 -0
- data/lib/sms_candy/helpers/get_message_status_helper.rb +40 -0
- data/lib/sms_candy/helpers/message_callback_renderer_helper.rb +20 -0
- data/lib/sms_candy/helpers/send_message_helper.rb +43 -0
- data/lib/sms_candy/manager.rb +34 -0
- data/lib/sms_candy/version.rb +3 -0
- data/lib/tasks/sms_candy_tasks.rake +4 -0
- data/spec/authentication/strategies/o_auth_2_spec.rb +10 -0
- data/spec/authentication/strategies/strategy_behaviors.rb +32 -0
- data/spec/authentication/strategies_spec.rb +38 -0
- data/spec/config_spec.rb +15 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +34 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/local_env.yml +4 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +56 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/log/test.log +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/helpers/common_methods.rb +16 -0
- data/spec/helpers/get_message_response_helper_spec.rb +13 -0
- data/spec/helpers/get_message_status_helper_spec.rb +30 -0
- data/spec/helpers/message_callback_renderer_helper_spec.rb +23 -0
- data/spec/helpers/send_message_helper_spec.rb +22 -0
- data/spec/manager_spec.rb +75 -0
- data/spec/spec_helper.rb +21 -0
- metadata +181 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: edc4fc03b0dcaf99dea9c019802f39ca54900491
|
4
|
+
data.tar.gz: 914cedaacb9ec271946bd4b518b5440798e26046
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 592e6f9036008b2cb5b9b295010e3348feefc7a2f376eeccf6db0cd7950cb8b4b6864221701bfb72038fed405c29cee9e11d4e2a6f4a614f8fb8900a4541c14c
|
7
|
+
data.tar.gz: 565fa33a2b60336198036c8fcaff70d258a38ab7e40c21096bce5c2fc3ea56d7708b50de059e3959516e512d9190312bf09a971826ca7a4ed03111e3de554d6b
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2015 Hadi Badjian
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'SmsCandy'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'lib'
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
task default: :test
|
data/lib/sms_candy.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
require 'sms_candy/config'
|
3
|
+
require 'sms_candy/authentication/strategies'
|
4
|
+
require 'sms_candy/helpers/send_message_helper'
|
5
|
+
require 'sms_candy/helpers/get_message_status_helper'
|
6
|
+
require 'sms_candy/helpers/get_message_response_helper'
|
7
|
+
require 'sms_candy/helpers/message_callback_renderer_helper'
|
8
|
+
require 'sms_candy/manager'
|
9
|
+
|
10
|
+
module SmsCandy
|
11
|
+
module Authentication
|
12
|
+
module Strategies
|
13
|
+
autoload :OAuth2, 'sms_candy/authentication/strategies/o_auth_2'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SmsCandy
|
2
|
+
|
3
|
+
module Authentication
|
4
|
+
|
5
|
+
module Strategies
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def add(label, strategy = nil)
|
10
|
+
strategy ||= SmsCandy::Authentication::Strategies::OAuth2.new
|
11
|
+
|
12
|
+
unless strategy.class.method_defined?(:authenticate!)
|
13
|
+
raise NoMethodError, "authenticate! is not declared in the #{label.inspect} strategy"
|
14
|
+
end
|
15
|
+
|
16
|
+
_strategies[label] = strategy
|
17
|
+
end
|
18
|
+
|
19
|
+
def clear!
|
20
|
+
_strategies.clear
|
21
|
+
end
|
22
|
+
|
23
|
+
def list
|
24
|
+
_strategies
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def _strategies
|
30
|
+
@strategies ||= {}
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module SmsCandy
|
2
|
+
|
3
|
+
module Authentication
|
4
|
+
|
5
|
+
module Strategies
|
6
|
+
|
7
|
+
class OAuth2
|
8
|
+
attr_reader :access_token
|
9
|
+
attr_reader :expires_at
|
10
|
+
|
11
|
+
#Obtain these keys from the Telstra Developer Portal
|
12
|
+
# APP_KEY="yourapikey"
|
13
|
+
# APP_SECRET="yourapisecret"
|
14
|
+
|
15
|
+
# curl "https://api.telstra.com/v1/oauth/token?client_id=$APP_KEY&client_secret=$APP_SECRET&grant_type=client_credentials&scope=SMS"
|
16
|
+
def authenticate!
|
17
|
+
response = RestClient.get(base_url, { params: authentication_params })
|
18
|
+
|
19
|
+
json_response = JSON.parse response.body
|
20
|
+
@expires_at = Time.zone.now + json_response["expires_in"].to_i
|
21
|
+
@access_token = json_response["access_token"]
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def authentication_params
|
27
|
+
{
|
28
|
+
client_id: ENV["TELSTRA_SMS_CLIENT_ID"],
|
29
|
+
client_secret: ENV["TELSTRA_SMS_CLIENT_SECRET"],
|
30
|
+
grant_type: ENV["TELSTRA_SMS_GRANT_TYPE"],
|
31
|
+
scope: ENV["TELSTRA_SMS_SCOPE"]
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def base_url
|
36
|
+
SmsCandy::Config.base_url + "oauth/token"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module SmsCandy
|
2
|
+
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
class GetMessageResponseHelper
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
# * MESSAGE_ID value is the value returned from a previous POST https://api.telstra.com/v1/sms/messages call
|
10
|
+
# * Authorization header value should be in the format of "Bearer xxx" where xxx is access token returned
|
11
|
+
# from a previous GET https://api.telstra.com/v1/oauth/token request.
|
12
|
+
# MESSAGE_ID="6F0B6030D7309137"
|
13
|
+
# TOKEN=your_token
|
14
|
+
|
15
|
+
# curl -H "Authorization: Bearer $TOKEN" \
|
16
|
+
# "https://api.telstra.com/v1/sms/messages/$MESSAGE_ID/response"
|
17
|
+
def response(message_id, access_token)
|
18
|
+
headers = {
|
19
|
+
"authorization" => "Bearer #{access_token}",
|
20
|
+
"Accept" => "application/json"
|
21
|
+
}
|
22
|
+
|
23
|
+
response = RestClient.get(_base_url(message_id), headers)
|
24
|
+
json_response = JSON.parse response
|
25
|
+
json_response.map { |response| response.map { |l,v| { l.underscore.to_sym => v } }.inject(:merge) }
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def _base_url(message_id)
|
31
|
+
SmsCandy::Config.base_url + "sms/messages/#{message_id}/response"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module SmsCandy
|
2
|
+
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
class GetMessageStatusHelper
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
# * MESSAGE_ID value is the value returned from a previous POST https://api.telstra.com/v1/sms/messages call
|
10
|
+
# * Authorization header value should be in the format of "Bearer xxx" where xxx is access token returned
|
11
|
+
# from a previous GET https://api.telstra.com/v1/oauth/token request.
|
12
|
+
# MESSAGE_ID="6F0B6030D7309137"
|
13
|
+
# TOKEN=your_token
|
14
|
+
|
15
|
+
# curl -H "Authorization: Bearer $TOKEN" \
|
16
|
+
# "https://api.telstra.com/v1/sms/messages/$MESSAGE_ID"
|
17
|
+
def status(message_id, access_token)
|
18
|
+
headers = {
|
19
|
+
"authorization" => "Bearer #{access_token}",
|
20
|
+
"Accept" => "application/json"
|
21
|
+
}
|
22
|
+
|
23
|
+
response = RestClient.get(_base_url(message_id), headers)
|
24
|
+
json_response = JSON.parse response
|
25
|
+
json_response.map { |l,v| { l.underscore.to_sym => v } }.inject(:merge)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def _base_url(message_id)
|
31
|
+
SmsCandy::Config.base_url + "sms/messages/#{message_id}"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SmsCandy
|
2
|
+
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
class MessageCallbackRendererHelper
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def render(message)
|
10
|
+
json_response = JSON.parse message
|
11
|
+
json_response.map { |l,v| { l.underscore.to_sym => v } }.inject(:merge)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module SmsCandy
|
2
|
+
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
class SendMessageHelper
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
# * Recipient number should be in the format of "04xxxxxxxx" where x is a digit
|
10
|
+
# * Authorization header value should be in the format of "Bearer xxx" where xxx is access token returned
|
11
|
+
# from a previous GET https://api.telstra.com/v1/oauth/token request.
|
12
|
+
# RECIPIENT_NUMBER=your_number
|
13
|
+
# TOKEN=your_token
|
14
|
+
|
15
|
+
# curl -H "Content-Type: application/json" \
|
16
|
+
# -H "Authorization: Bearer $TOKEN" \
|
17
|
+
# -d "{\"to\":\"$RECIPIENT_NUMBER\", \"body\":\"Hello!\"}" \
|
18
|
+
# "https://api.telstra.com/v1/sms/messages"
|
19
|
+
def send_sms(number, message, access_token)
|
20
|
+
headers = {
|
21
|
+
"authorization" => "Bearer #{access_token}",
|
22
|
+
"Content-Type" => "application/json",
|
23
|
+
"Accept" => "application/json"
|
24
|
+
}
|
25
|
+
data = { to: number, body: message }.to_json
|
26
|
+
|
27
|
+
response = RestClient.post(_base_url, data, headers)
|
28
|
+
JSON.parse(response)['messageId']
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def _base_url
|
34
|
+
SmsCandy::Config.base_url + "sms/messages"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module SmsCandy
|
2
|
+
|
3
|
+
class Manager
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def authenticate
|
8
|
+
SmsCandy::Authentication::Strategies.list.each do |label, strategy|
|
9
|
+
access_token = strategy.authenticate!
|
10
|
+
return access_token if access_token
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def send_sms(number, message, access_token)
|
15
|
+
SmsCandy::Helpers::SendMessageHelper.send_sms(number, message, access_token)
|
16
|
+
end
|
17
|
+
|
18
|
+
def message_status(message_id, access_token)
|
19
|
+
SmsCandy::Helpers::GetMessageStatusHelper.status(message_id, access_token)
|
20
|
+
end
|
21
|
+
|
22
|
+
def message_response(message_id, access_token)
|
23
|
+
SmsCandy::Helpers::GetMessageResponseHelper.response(message_id, access_token)
|
24
|
+
end
|
25
|
+
|
26
|
+
def message_callback(message)
|
27
|
+
SmsCandy::Helpers::MessageCallbackRendererHelper.render(message)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'authentication/strategies/strategy_behaviors'
|
3
|
+
|
4
|
+
RSpec.describe SmsCandy::Authentication::Strategies::OAuth2 do
|
5
|
+
|
6
|
+
let(:subject) { SmsCandy::Authentication::Strategies::OAuth2.new }
|
7
|
+
|
8
|
+
it_behaves_like "an authentication strategy"
|
9
|
+
|
10
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
shared_examples_for "an authentication strategy" do
|
2
|
+
let(:strategy) { described_class.new }
|
3
|
+
|
4
|
+
context "interface" do
|
5
|
+
|
6
|
+
it "should respond to authenticate!" do
|
7
|
+
expect(strategy).to respond_to(:authenticate!)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have access_token reader" do
|
11
|
+
expect(strategy).to respond_to(:access_token)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have expires_at reader" do
|
15
|
+
expect(strategy).to respond_to(:expires_at)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should not have access_token writer" do
|
19
|
+
expect {
|
20
|
+
strategy.access_token = ""
|
21
|
+
}.to raise_error
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should not have expires_at writer" do
|
25
|
+
expect {
|
26
|
+
strategy.expires_at = Time.zone.now
|
27
|
+
}.to raise_error
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class InvalidStrategyWithoutAuthenticate
|
4
|
+
end
|
5
|
+
|
6
|
+
RSpec.describe SmsCandy::Authentication::Strategies do
|
7
|
+
|
8
|
+
let(:subject) { SmsCandy::Authentication::Strategies }
|
9
|
+
let(:strategy) { SmsCandy::Authentication::Strategies::OAuth2.new }
|
10
|
+
|
11
|
+
after(:each) do
|
12
|
+
SmsCandy::Authentication::Strategies.clear!
|
13
|
+
end
|
14
|
+
|
15
|
+
context "#add" do
|
16
|
+
|
17
|
+
it "should add new strategies" do
|
18
|
+
expect(subject.add :oauth2, strategy).to eql(strategy)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should fail adding a strategy without authenticate! method" do
|
22
|
+
expect {
|
23
|
+
subject.add :invalid_strategy, InvalidStrategyWithoutAuthenticate.new
|
24
|
+
}.to raise_error
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
context "#clear!" do
|
30
|
+
|
31
|
+
it "should be able to clear strategies" do
|
32
|
+
subject.add :oauth2, strategy
|
33
|
+
expect(subject.clear!).to eql({})
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|