aftalk 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 +7 -0
- data/.circleci/config.yml +45 -0
- data/.gitignore +35 -0
- data/.rubocop.yml +630 -0
- data/Gemfile +3 -0
- data/README.md +69 -0
- data/Rakefile +1 -0
- data/africas_talking_api.gemspec +27 -0
- data/lib/aftalk.rb +13 -0
- data/lib/aftalk/client.rb +20 -0
- data/lib/aftalk/configuration.rb +37 -0
- data/lib/aftalk/connection.rb +37 -0
- data/lib/aftalk/exceptions.rb +5 -0
- data/lib/aftalk/request.rb +41 -0
- data/lib/aftalk/response.rb +26 -0
- data/lib/aftalk/responses/send_message_response.rb +21 -0
- data/lib/aftalk/sms_message_status.rb +12 -0
- data/lib/aftalk/version.rb +3 -0
- data/spec/aftalk_spec.rb +7 -0
- data/spec/integration/send_message_spec.rb +25 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/env_vars.rb +13 -0
- data/spec/unit/client_spec.rb +46 -0
- data/spec/unit/configuration_spec.rb +73 -0
- data/spec/unit/connection_spec.rb +67 -0
- data/spec/unit/request_spec.rb +59 -0
- data/spec/unit/response_spec.rb +27 -0
- data/spec/unit/responses/send_message_response_spec.rb +43 -0
- data/spec/unit/sms_message_status_spec.rb +23 -0
- metadata +195 -0
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
[](https://circleci.com/gh/thriveglobal/aftalk)
|
2
|
+
[](https://codeclimate.com/github/thriveglobal/aftalk/maintainability)
|
3
|
+
|
4
|
+
# aftalk
|
5
|
+
|
6
|
+
A Ruby wrapper for [Africa's Talking](https://africastalking.com) telephony services.
|
7
|
+
|
8
|
+
Currently the only endpoint supported is the `/messaging` POST endpoint that allows you to send SMS messages. Support for other endpoints is planned for future releases.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add
|
13
|
+
|
14
|
+
> `gem "aftalk"`
|
15
|
+
|
16
|
+
to your `Gemfile` and run
|
17
|
+
|
18
|
+
> `$ bundle`
|
19
|
+
|
20
|
+
or install manually by running
|
21
|
+
|
22
|
+
> `gem install aftalk`.
|
23
|
+
|
24
|
+
## Configuration
|
25
|
+
|
26
|
+
Africa's Talking needs a username and an API key to authenticate your requests. You can also toggle sandbox mode, which causes the gem to hit AF's sandbox host instead of the live API. (**Note: Use of sandbox mode requires that you provide the username `sandbox`.** This is not well documented.)
|
27
|
+
|
28
|
+
There are two ways to configure these options.
|
29
|
+
|
30
|
+
### Set environment variables
|
31
|
+
|
32
|
+
The first way to configure `aftalk` is by setting environment variables. You can do this by creating a file called `.env` in your project root and adding content like this to it:
|
33
|
+
|
34
|
+
```txt
|
35
|
+
AFRICAS_TALKING_API_KEY=abc123
|
36
|
+
AFRICAS_TALKING_USER_NAME=sandbox
|
37
|
+
AFRICAS_TALKING_SANDBOX=true
|
38
|
+
```
|
39
|
+
|
40
|
+
Replace the values with your own data. Sandbox mode will be activated if any value whatsoever is provided for `AFRICAS_TALKING_SANDBOX`. If you don't want to use sandbox mode, don't set this variable at all.
|
41
|
+
|
42
|
+
### Use `AfTalk::Configuration`
|
43
|
+
|
44
|
+
You can also configure this data in Ruby code as follows:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
AfTalk::Configuration.configure do |config|
|
48
|
+
config.api_key = "abc123"
|
49
|
+
config.sandbox = true
|
50
|
+
config.user_name = "sandbox"
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
Make sure this code runs before you try to use the API.
|
55
|
+
|
56
|
+
## Usage
|
57
|
+
|
58
|
+
To send an SMS:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
phone_number = "+15555555555"
|
62
|
+
message = "Hello, world!"
|
63
|
+
|
64
|
+
AfTalk.send_message(
|
65
|
+
to: phone_number,
|
66
|
+
message: message,
|
67
|
+
)
|
68
|
+
```
|
69
|
+
The `send_message` method requires `to` and `message` parameters, but also supports all optional parameters supported by the API. For a full list, see [the API docs](http://docs.africastalking.com/sms/sending/).
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "aftalk/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "aftalk"
|
7
|
+
spec.version = AfTalk::VERSION
|
8
|
+
spec.email = ["techadmin@thriveglobal.com"]
|
9
|
+
spec.summary = "API wrapper for Africa's Talking services"
|
10
|
+
spec.description = <<-DESC
|
11
|
+
A Ruby wrapper for Africa's Talking telephony services (https://africastalking.com)
|
12
|
+
DESC
|
13
|
+
spec.homepage = "https://github.com/thriveglobal/africas_talking_api"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.authors = ["Thrive Global Engineering Team"]
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
20
|
+
spec.add_dependency "dotenv", "~>2.2"
|
21
|
+
spec.add_dependency "faraday", "~> 0.13"
|
22
|
+
spec.add_dependency "faraday_middleware", "~> 0.12"
|
23
|
+
spec.add_development_dependency "rake", "~> 12"
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.7"
|
25
|
+
spec.add_development_dependency "rspec_junit_formatter", "~> 0.3"
|
26
|
+
spec.add_development_dependency "vcr", "~> 3.0"
|
27
|
+
end
|
data/lib/aftalk.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "dotenv"
|
2
|
+
Dotenv.load
|
3
|
+
|
4
|
+
APP_PATH = File.expand_path("../", __FILE__) + "/aftalk"
|
5
|
+
|
6
|
+
module AfTalk
|
7
|
+
autoload(:Response, "#{APP_PATH}/response.rb")
|
8
|
+
autoload(:Client, "#{APP_PATH}/client.rb")
|
9
|
+
|
10
|
+
extend AfTalk::Client
|
11
|
+
end
|
12
|
+
|
13
|
+
Dir["#{APP_PATH}/**/*.rb"].each { |file| require file }
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module AfTalk
|
2
|
+
module Client
|
3
|
+
def send_message(to:, message:, **options)
|
4
|
+
messaging_params = { to: to, message: message }.merge(options)
|
5
|
+
response = post("/messaging", messaging_params)
|
6
|
+
|
7
|
+
AfTalk::SendMessageResponse.new(response)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def post(endpoint, **options)
|
13
|
+
request.post(endpoint, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def request
|
17
|
+
AfTalk::Request
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module AfTalk
|
2
|
+
class Configuration
|
3
|
+
DEFAULT_VERSION = "version1".freeze
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_writer :api_key, :sandbox, :user_name
|
7
|
+
|
8
|
+
def configure
|
9
|
+
yield self
|
10
|
+
connection.clear
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def version
|
15
|
+
DEFAULT_VERSION
|
16
|
+
end
|
17
|
+
|
18
|
+
def api_key
|
19
|
+
@api_key || ENV["AFRICAS_TALKING_API_KEY"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def sandbox
|
23
|
+
@sandbox || ENV["AFRICAS_TALKING_SANDBOX"]
|
24
|
+
end
|
25
|
+
|
26
|
+
def user_name
|
27
|
+
@user_name || ENV["AFRICAS_TALKING_USER_NAME"]
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def connection
|
33
|
+
AfTalk::Connection
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "faraday"
|
2
|
+
require "faraday_middleware"
|
3
|
+
|
4
|
+
module AfTalk
|
5
|
+
class Connection
|
6
|
+
URL = "https://api.africastalking.com".freeze
|
7
|
+
SANDBOX_URL = "https://api.sandbox.africastalking.com".freeze
|
8
|
+
|
9
|
+
def self.build
|
10
|
+
@_connection ||= new.build
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.clear
|
14
|
+
@_connection = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def build
|
18
|
+
Faraday.new(
|
19
|
+
url: api_url,
|
20
|
+
headers: { apiKey: AfTalk::Configuration.api_key,
|
21
|
+
accept: "application/json" },
|
22
|
+
) do |connection|
|
23
|
+
connection.request :url_encoded
|
24
|
+
connection.response :json,
|
25
|
+
content_type: /\bjson$/,
|
26
|
+
parser_options: { symbolize_names: true }
|
27
|
+
connection.adapter Faraday.default_adapter
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def api_url
|
34
|
+
ENV["AFRICAS_TALKING_SANDBOX"] ? SANDBOX_URL : URL
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module AfTalk
|
2
|
+
class Request
|
3
|
+
def self.get(path, **options)
|
4
|
+
new.get(path, options)
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.post(path, **options)
|
8
|
+
new.post(path, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(path, **options)
|
12
|
+
connection.get(complete_path(path), complete_options(options))
|
13
|
+
end
|
14
|
+
|
15
|
+
def post(path, **options)
|
16
|
+
connection.post(complete_path(path), complete_options(options))
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def complete_options(options)
|
22
|
+
default_options.merge(options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def complete_path(path)
|
26
|
+
"/#{configuration.version}#{path}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def configuration
|
30
|
+
AfTalk::Configuration
|
31
|
+
end
|
32
|
+
|
33
|
+
def connection
|
34
|
+
AfTalk::Connection.build
|
35
|
+
end
|
36
|
+
|
37
|
+
def default_options
|
38
|
+
{ username: configuration.user_name }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module AfTalk
|
2
|
+
class Response
|
3
|
+
SUCCESS_STATUSES = [200, 201].freeze
|
4
|
+
JSON_CONTENT = /\bjson$/
|
5
|
+
|
6
|
+
attr_reader :body, :error_message, :status
|
7
|
+
|
8
|
+
def initialize(response)
|
9
|
+
@status = response.status
|
10
|
+
body = response.body
|
11
|
+
|
12
|
+
if success?
|
13
|
+
@body = body
|
14
|
+
else
|
15
|
+
@error_message = body
|
16
|
+
@body = { error: body }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def success?
|
23
|
+
SUCCESS_STATUSES.include?(status)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module AfTalk
|
2
|
+
class SendMessageResponse < Response
|
3
|
+
attr_reader :message, :recipients
|
4
|
+
|
5
|
+
def initialize(response)
|
6
|
+
super(response)
|
7
|
+
if success?
|
8
|
+
@message = body_data[:Message]
|
9
|
+
@recipients = body_data[:Recipients].map do |recipient|
|
10
|
+
SmsMessageStatus.new(recipient)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def body_data
|
18
|
+
body[:SMSMessageData]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/spec/aftalk_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe AfTalk do
|
4
|
+
describe "#send_message" do
|
5
|
+
let(:options) do
|
6
|
+
{ to: "+14125554563", message: "Hello World!", foo: "bar" }
|
7
|
+
end
|
8
|
+
|
9
|
+
it "produces a send message response object" do
|
10
|
+
VCR.use_cassette("aftalk/messaging/send_message") do
|
11
|
+
response = described_class.send_message(options)
|
12
|
+
recipient = response.recipients.first
|
13
|
+
|
14
|
+
expect(response.error_message).to be_nil
|
15
|
+
expect(response.status).to eq(201)
|
16
|
+
expect(response.message).to be
|
17
|
+
|
18
|
+
expect(recipient.cost).to be
|
19
|
+
expect(recipient.message_id).to be
|
20
|
+
expect(recipient.number).to eq(options[:to])
|
21
|
+
expect(recipient.status).to eq("Success")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "rspec"
|
2
|
+
require "aftalk"
|
3
|
+
require "vcr"
|
4
|
+
|
5
|
+
current_path = File.expand_path("../", __FILE__)
|
6
|
+
Dir["#{current_path}/support/**/*.rb"].each { |file| require file }
|
7
|
+
|
8
|
+
VCR.configure do |config|
|
9
|
+
config.cassette_library_dir = "spec/support/vcr_cassettes"
|
10
|
+
config.hook_into :faraday
|
11
|
+
config.configure_rspec_metadata!
|
12
|
+
config.allow_http_connections_when_no_cassette = false
|
13
|
+
config.default_cassette_options = { record: :new_episodes }
|
14
|
+
end
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.before :each do
|
18
|
+
AfTalk::Connection.clear
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module EnvVars
|
2
|
+
def stub_env_vars(vars)
|
3
|
+
allow(ENV).to receive(:[]).and_call_original
|
4
|
+
|
5
|
+
vars.each do |key, val|
|
6
|
+
allow(ENV).to receive(:[]).with(key).and_return(val)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.include EnvVars
|
13
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe AfTalk::Client do
|
4
|
+
subject do
|
5
|
+
class ExampleKlass
|
6
|
+
extend AfTalk::Client
|
7
|
+
end
|
8
|
+
ExampleKlass
|
9
|
+
end
|
10
|
+
|
11
|
+
before(:all) { reset_config }
|
12
|
+
|
13
|
+
describe "#send_message" do
|
14
|
+
let(:options) {
|
15
|
+
{ to: "+14125554563", message: "Hello World!", foo: "bar" }
|
16
|
+
}
|
17
|
+
let(:response) { double(headers: { "content-type" => "application/json" }) }
|
18
|
+
|
19
|
+
context "with all required options" do
|
20
|
+
it "it instantiates a send message response" do
|
21
|
+
allow(AfTalk::Request).to receive(:post).
|
22
|
+
with("/messaging", options).
|
23
|
+
and_return(response)
|
24
|
+
allow(AfTalk::SendMessageResponse).to receive(:new).with(response)
|
25
|
+
|
26
|
+
subject.send_message(options)
|
27
|
+
|
28
|
+
expect(AfTalk::SendMessageResponse).to have_received(:new).with(response)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "without some required options" do
|
33
|
+
let(:options) { {} }
|
34
|
+
|
35
|
+
it "raises a missing options error" do
|
36
|
+
expect { subject.send_message(options) }.to raise_error(ArgumentError)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def reset_config
|
42
|
+
config = AfTalk::Configuration
|
43
|
+
config.api_key = nil
|
44
|
+
config.user_name = nil
|
45
|
+
end
|
46
|
+
end
|