dcm_client 0.2.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/Gemfile +4 -0
- data/README.md +27 -0
- data/Rakefile +7 -0
- data/dcm_client.gemspec +34 -0
- data/lib/dcm_client.rb +16 -0
- data/lib/dcm_client/client.rb +69 -0
- data/lib/dcm_client/connection.rb +56 -0
- data/lib/dcm_client/error.rb +36 -0
- data/lib/dcm_client/version.rb +4 -0
- data/spec/client_spec.rb +54 -0
- data/spec/connection_spec.rb +13 -0
- data/spec/dcm_client_spec.rb +7 -0
- data/spec/error_spec.rb +34 -0
- data/spec/spec_helper.rb +20 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 13acb0bebdf78585fceca974dd5f59a9ec9248da
|
4
|
+
data.tar.gz: 77c314e7f8e34d8507f67c4c8ec31f39c3b5d64a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9a427ccea1e81916c4b524b00c838e7b68be26da0130b6e28ee951767ade8b4e2fde1d820719a77f583647aac395ee6ef94c1880daa00b0664ce1680acd0658d
|
7
|
+
data.tar.gz: 643d5a4d6fffd4e8cf100f4033f6a1fbef3c9e543383bb9858c483a2b5319952b9082a0df95ab375146b9b7f1617b7df7e48a9f9943b5f9dc00e27d6e2527881
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# DCMClient
|
2
|
+
|
3
|
+
DCMClient is a Ruby library to interact with GovDelivery's Digital Communication Management REST API.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'dcm_client'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install dcm_client
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
## Contributing
|
22
|
+
|
23
|
+
1. Fork it
|
24
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
25
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
26
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
27
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/dcm_client.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "dcm_client/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "dcm_client"
|
7
|
+
s.version = DCMClient::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["GovDelivery"]
|
10
|
+
s.email = ["support@govdelivery.com"]
|
11
|
+
s.homepage = "http://govdelivery.com"
|
12
|
+
s.summary = %q{A ruby client to interact with the GovDelivery DCM REST API.}
|
13
|
+
s.description = %q{A reference implementation, written in Ruby, to interact with GovDelivery's DCM API.}
|
14
|
+
|
15
|
+
if RUBY_VERSION < "1.9"
|
16
|
+
s.add_runtime_dependency "json" # this is part of 1.9's stdlib
|
17
|
+
end
|
18
|
+
|
19
|
+
s.add_runtime_dependency "faraday"
|
20
|
+
s.add_runtime_dependency "faraday_middleware"
|
21
|
+
s.add_development_dependency "rspec"
|
22
|
+
s.add_development_dependency "rake"
|
23
|
+
|
24
|
+
s.files = %w{
|
25
|
+
Gemfile
|
26
|
+
README.md
|
27
|
+
Rakefile
|
28
|
+
dcm_client.gemspec
|
29
|
+
}.concat(Dir["lib/**/*"])
|
30
|
+
|
31
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
32
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
end
|
data/lib/dcm_client.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# stdlib
|
2
|
+
require 'base64'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
# gems
|
6
|
+
require 'faraday'
|
7
|
+
require 'faraday_middleware'
|
8
|
+
|
9
|
+
# project
|
10
|
+
require 'dcm_client/version'
|
11
|
+
require 'dcm_client/connection'
|
12
|
+
require 'dcm_client/client'
|
13
|
+
require 'dcm_client/error'
|
14
|
+
|
15
|
+
module DCMClient
|
16
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module DCMClient
|
2
|
+
class Client
|
3
|
+
UNSUBSCRIBE_URI = '/api/account/%s/subscribers/%s'
|
4
|
+
SUBSCRIBE_URI = '/api/account/%s/subscribers/add_subscriptions'
|
5
|
+
|
6
|
+
attr_reader :connection
|
7
|
+
|
8
|
+
##
|
9
|
+
# Create and return a new client with an established connection to DCM.
|
10
|
+
#
|
11
|
+
# :username
|
12
|
+
# :password
|
13
|
+
# :api_root - fully-qualified root URL to DCM, including protocol and (optional) port.
|
14
|
+
#
|
15
|
+
def initialize(opts={})
|
16
|
+
@connection = Connection.new(opts)
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Completely remove a wireless subscriber from an account.
|
21
|
+
# number - String - the wireless number (with country code), formatted as CC+NUMBER
|
22
|
+
# Ex: 1+4444444444
|
23
|
+
# account - String - the account code
|
24
|
+
#
|
25
|
+
def delete_wireless_subscriber(number, account)
|
26
|
+
number = Base64.encode64(number).strip
|
27
|
+
@connection.delete(UNSUBSCRIBE_URI % [account, number])
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Add one or more subscriptions to a wireless subscriber. If the subscriber doesn't exist it will be created.
|
32
|
+
# number - String - the wireless number (with country code), formatted as CC+NUMBER
|
33
|
+
# Ex: 1+4444444444
|
34
|
+
# account - String - the account code
|
35
|
+
# topics - Array - the topics to subscribe
|
36
|
+
# send_notifications - Boolean - whether DCM should send the subscriber a notification (defaults to true)
|
37
|
+
#
|
38
|
+
def wireless_subscribe(number, account, topics, send_notifications=true)
|
39
|
+
country_code, rest = number.split('+')
|
40
|
+
sub = {
|
41
|
+
:subscriber => {
|
42
|
+
:phone => rest,
|
43
|
+
:country_code => country_code,
|
44
|
+
:send_notifications => send_notifications,
|
45
|
+
:topics => topics.map{|code| {:code => code} }
|
46
|
+
}
|
47
|
+
}
|
48
|
+
@connection.post(SUBSCRIBE_URI % account, sub)
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Add one or more subscriptions to a wireless subscriber. If the subscriber doesn't exist it will be created.
|
53
|
+
# email - String - the email address to subscribe
|
54
|
+
# account - String - the account code
|
55
|
+
# topics - Array - the topics to subscribe
|
56
|
+
# send_notifications - Boolean - whether DCM should send the subscriber a notification (defaults to true)
|
57
|
+
#
|
58
|
+
def email_subscribe(email, account, topics, send_notifications=true)
|
59
|
+
sub = {
|
60
|
+
:subscriber => {
|
61
|
+
:email => email,
|
62
|
+
:send_notifications => send_notifications,
|
63
|
+
:topics => topics.map{|code| {:code => code} }
|
64
|
+
}
|
65
|
+
}
|
66
|
+
@connection.post(SUBSCRIBE_URI % account, sub)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module DCMClient
|
2
|
+
class Connection
|
3
|
+
attr_accessor :username, :password, :api_root, :connection, :logger
|
4
|
+
|
5
|
+
def post(href, body)
|
6
|
+
wrap do
|
7
|
+
connection.post do |req|
|
8
|
+
req.url href
|
9
|
+
req.body = body.to_json
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def delete(href)
|
15
|
+
wrap do
|
16
|
+
connection.delete("#{href}.json")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(opts={})
|
21
|
+
self.username = opts[:username]
|
22
|
+
self.password = opts[:password]
|
23
|
+
self.api_root = opts[:api_root]
|
24
|
+
self.logger = opts[:logger]
|
25
|
+
end
|
26
|
+
|
27
|
+
def connection
|
28
|
+
@connection ||= Faraday.new(:url => self.api_root) do |faraday|
|
29
|
+
faraday.use Faraday::Response::Logger, self.logger if self.logger
|
30
|
+
faraday.use Faraday::Response::RaiseError
|
31
|
+
faraday.headers[:user_agent] = DCMClient::USER_AGENT
|
32
|
+
faraday.request :json
|
33
|
+
faraday.basic_auth(self.username, self.password)
|
34
|
+
faraday.response :json, :content_type => /\bjson$/
|
35
|
+
faraday.adapter Faraday.default_adapter
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def wrap
|
42
|
+
yield
|
43
|
+
rescue Faraday::Error::ResourceNotFound => e
|
44
|
+
raise DCMClient::Error::NotFound.new(e)
|
45
|
+
rescue Faraday::Error::ClientError => e
|
46
|
+
if e.response.respond_to?(:[]) && e.response[:status] == 422
|
47
|
+
raise DCMClient::Error::UnprocessableEntity.new(e)
|
48
|
+
else
|
49
|
+
raise DCMClient::Error.new(e)
|
50
|
+
end
|
51
|
+
rescue Exception => e
|
52
|
+
raise DCMClient::Error.new(e)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module DCMClient
|
4
|
+
# A parent error class makes it easier to trap any error
|
5
|
+
# from client code.
|
6
|
+
class Error < StandardError
|
7
|
+
attr_reader :response
|
8
|
+
|
9
|
+
def initialize(arg, response=nil)
|
10
|
+
self.response = response
|
11
|
+
if (arg.respond_to?(:backtrace))
|
12
|
+
super(arg.message)
|
13
|
+
self.set_backtrace(arg.backtrace)
|
14
|
+
if (arg.respond_to?(:response))
|
15
|
+
self.response = arg.response
|
16
|
+
end
|
17
|
+
else
|
18
|
+
super(arg.to_s)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Faraday responses aren't actually of class Faraday::Response, which is annoying.
|
23
|
+
# This makes them respond the the methods we need in XACT!
|
24
|
+
def response=(_response)
|
25
|
+
@response = _response.is_a?(Hash) ? OpenStruct.new(_response) : _response
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Specific error types
|
30
|
+
class Error::NotFound < Error
|
31
|
+
end
|
32
|
+
class Error::TimeoutError < Error
|
33
|
+
end
|
34
|
+
class Error::UnprocessableEntity < Error
|
35
|
+
end
|
36
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DCMClient::Client do
|
4
|
+
subject {
|
5
|
+
client = DCMClient::Client.new({})
|
6
|
+
}
|
7
|
+
|
8
|
+
it 'should invoke delete correctly' do
|
9
|
+
number = "+15555555555"
|
10
|
+
account = 'ACME'
|
11
|
+
|
12
|
+
connection = double('connection')
|
13
|
+
sub = Base64.encode64(number).strip
|
14
|
+
connection.should_receive(:delete).with("/api/account/#{account}/subscribers/#{sub}")
|
15
|
+
DCMClient::Connection.stub(:new).and_return(connection)
|
16
|
+
subject.delete_wireless_subscriber(number, account)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should invoke wireless update correctly' do
|
20
|
+
number = '5555555555'
|
21
|
+
country_code = '1'
|
22
|
+
account = 'ACME'
|
23
|
+
|
24
|
+
connection = double('connection')
|
25
|
+
sub = {
|
26
|
+
:subscriber => {
|
27
|
+
:phone => '5' * 10,
|
28
|
+
:country_code => '1',
|
29
|
+
:send_notifications => true,
|
30
|
+
:topics => [{:code => 'TOPIC'}]
|
31
|
+
}
|
32
|
+
}
|
33
|
+
connection.should_receive(:post).with("/api/account/#{account}/subscribers/add_subscriptions", sub)
|
34
|
+
DCMClient::Connection.stub(:new).and_return(connection)
|
35
|
+
subject.wireless_subscribe("#{country_code}+#{number}", account, ['TOPIC'])
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should invoke update correctly' do
|
39
|
+
email = 'joe@example.com'
|
40
|
+
account = 'ACME'
|
41
|
+
|
42
|
+
connection = double('connection')
|
43
|
+
sub = {
|
44
|
+
:subscriber => {
|
45
|
+
:email => email,
|
46
|
+
:send_notifications => true,
|
47
|
+
:topics => [{:code => 'TOPIC'}]
|
48
|
+
}
|
49
|
+
}
|
50
|
+
connection.should_receive(:post).with("/api/account/#{account}/subscribers/add_subscriptions", sub)
|
51
|
+
DCMClient::Connection.stub(:new).and_return(connection)
|
52
|
+
subject.email_subscribe(email, account, ['TOPIC'])
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DCMClient::Connection do
|
4
|
+
it 'intercepts 422 errors and raises DCMClient::UnprocessableEntity' do
|
5
|
+
conn = DCMClient::Connection.new
|
6
|
+
faraday = double('Faraday Connection')
|
7
|
+
error = Faraday::Error::ClientError.new(:status => 422)
|
8
|
+
faraday.stub(:post).and_raise(error)
|
9
|
+
conn.stub(:connection).and_return(faraday)
|
10
|
+
lambda {conn.post('hello', 'body')}.should raise_error(DCMClient::Error::UnprocessableEntity)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
data/spec/error_spec.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
Struct.new("TestResponseError", :message, :backtrace, :response)
|
4
|
+
|
5
|
+
describe DCMClient::Error do
|
6
|
+
let(:exception) { e = Exception.new("foo"); e.set_backtrace(['/Users/jim/workspace/my_project/ruby_file.rb']); e }
|
7
|
+
let(:response_exception) { Struct::TestResponseError.new("Hi", ['1'], {:status => 422}) }
|
8
|
+
|
9
|
+
context 'wrapping an exception' do
|
10
|
+
before { @error = DCMClient::Error.new(exception) }
|
11
|
+
specify { @error.backtrace.should eq(exception.backtrace) }
|
12
|
+
specify { @error.message.should eq(exception.message) }
|
13
|
+
specify { @error.response.should be(nil) }
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'wrapping a client exception' do
|
17
|
+
before { @error = DCMClient::Error.new(response_exception) }
|
18
|
+
specify { @error.backtrace.should eq(response_exception.backtrace) }
|
19
|
+
specify { @error.message.should eq(response_exception.message) }
|
20
|
+
specify { @error.response.should be_a(OpenStruct) }
|
21
|
+
specify { @error.response.status.should eq(422) }
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
context "a regular old exception" do
|
26
|
+
before { @error = DCMClient::Error.new("foo") }
|
27
|
+
specify { @error.message.should eq("foo") }
|
28
|
+
end
|
29
|
+
|
30
|
+
context '#response=' do
|
31
|
+
before { @error = DCMClient::Error.new("foo", :bar) }
|
32
|
+
specify { @error.response.should eq(:bar) }
|
33
|
+
end
|
34
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
$: << File.expand_path("../lib", __FILE__)
|
2
|
+
require 'dcm_client'
|
3
|
+
|
4
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
5
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
6
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
7
|
+
# loaded once.
|
8
|
+
#
|
9
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
12
|
+
config.run_all_when_everything_filtered = true
|
13
|
+
config.filter_run :focus
|
14
|
+
|
15
|
+
# Run specs in random order to surface order dependencies. If you find an
|
16
|
+
# order dependency and want to debug it, you can fix the order by providing
|
17
|
+
# the seed, which is printed after each run.
|
18
|
+
# --seed 1234
|
19
|
+
config.order = 'random'
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dcm_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- GovDelivery
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-17 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: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday_middleware
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: A reference implementation, written in Ruby, to interact with GovDelivery's
|
70
|
+
DCM API.
|
71
|
+
email:
|
72
|
+
- support@govdelivery.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- Gemfile
|
78
|
+
- README.md
|
79
|
+
- Rakefile
|
80
|
+
- dcm_client.gemspec
|
81
|
+
- lib/dcm_client.rb
|
82
|
+
- lib/dcm_client/client.rb
|
83
|
+
- lib/dcm_client/connection.rb
|
84
|
+
- lib/dcm_client/error.rb
|
85
|
+
- lib/dcm_client/version.rb
|
86
|
+
- spec/client_spec.rb
|
87
|
+
- spec/connection_spec.rb
|
88
|
+
- spec/dcm_client_spec.rb
|
89
|
+
- spec/error_spec.rb
|
90
|
+
- spec/spec_helper.rb
|
91
|
+
homepage: http://govdelivery.com
|
92
|
+
licenses: []
|
93
|
+
metadata: {}
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
requirements: []
|
109
|
+
rubyforge_project:
|
110
|
+
rubygems_version: 2.2.2
|
111
|
+
signing_key:
|
112
|
+
specification_version: 4
|
113
|
+
summary: A ruby client to interact with the GovDelivery DCM REST API.
|
114
|
+
test_files:
|
115
|
+
- spec/client_spec.rb
|
116
|
+
- spec/connection_spec.rb
|
117
|
+
- spec/dcm_client_spec.rb
|
118
|
+
- spec/error_spec.rb
|
119
|
+
- spec/spec_helper.rb
|