Skip to content

Commit

Permalink
add v3 - groups and unsubscripbe
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Klimenkov committed Feb 4, 2016
1 parent 48a32b7 commit 9660316
Show file tree
Hide file tree
Showing 15 changed files with 319 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .rvmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export RUBYOPT="rubygems"
export RUBYLIB="."
rvm 1.9.3@sendgrid_toolkit --create
rvm 2.2.3@sendgrid_toolkit --create
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ group :development do
gem "fakeweb", "~> 1.3.0"
gem "jeweler"
gem "rspec", "~> 2.7.0"
gem 'pry', '~> 0.10.3'
end
11 changes: 11 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ GEM
specs:
addressable (2.3.5)
builder (3.2.2)
coderay (1.1.0)
diff-lcs (1.1.3)
fakeweb (1.3.0)
faraday (0.8.8)
Expand Down Expand Up @@ -33,6 +34,7 @@ GEM
json (1.8.1)
jwt (0.1.8)
multi_json (>= 1.5)
method_source (0.8.2)
multi_json (1.8.2)
multi_xml (0.5.5)
multipart-post (1.2.0)
Expand All @@ -44,6 +46,10 @@ GEM
multi_json (~> 1.0)
multi_xml (~> 0.5)
rack (~> 1.2)
pry (0.10.3)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
rack (1.5.2)
rake (10.1.0)
rdoc (4.0.1)
Expand All @@ -56,6 +62,7 @@ GEM
rspec-expectations (2.7.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.7.0)
slop (3.6.0)

PLATFORMS
ruby
Expand All @@ -64,4 +71,8 @@ DEPENDENCIES
fakeweb (~> 1.3.0)
httparty (>= 0.7.6)
jeweler
pry (~> 0.10.3)
rspec (~> 2.7.0)

BUNDLED WITH
1.11.2
7 changes: 6 additions & 1 deletion lib/sendgrid_toolkit.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'httparty'
require 'pry'

require 'sendgrid_toolkit/abstract_sendgrid_client'
require 'sendgrid_toolkit/common'
Expand All @@ -11,6 +12,10 @@
require 'sendgrid_toolkit/invalid_emails'
require 'sendgrid_toolkit/mail'

require 'sendgrid_toolkit/v3/abstract_sendgrid_client'
require 'sendgrid_toolkit/v3/groups'
require 'sendgrid_toolkit/v3/unsubscribes'

require 'sendgrid_toolkit/newsletter/newsletter_sendgrid_client'
require 'sendgrid_toolkit/newsletter/lists'
require 'sendgrid_toolkit/newsletter/list_emails'
Expand All @@ -21,9 +26,9 @@ module SendgridToolkit
class << self
attr_accessor :api_user, :api_key
attr_writer :base_uri

def base_uri
@base_uri || ENV['SENDGRID_API_BASE_URI'] || BASE_URI
end
end

end
2 changes: 2 additions & 0 deletions lib/sendgrid_toolkit/sendgrid_error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ class SendEmailError < StandardError; end
class EmailDoesNotExist < StandardError; end
class SendgridServerError < StandardError; end
class APIError < StandardError; end
class GroupsError < StandardError; end
class NoGroupIdSpecified < StandardError; end
end
61 changes: 61 additions & 0 deletions lib/sendgrid_toolkit/v3/abstract_sendgrid_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
module SendgridToolkit
module V3
class AbstractSendgridClient

BASE_URI = 'https://api.sendgrid.com/v3/asm'

def initialize(api_user = nil, api_key = nil)
@api_user = api_user || SendgridToolkit.api_user || ENV['SMTP_USERNAME']
@api_key = api_key || SendgridToolkit.api_key || ENV['SMTP_PASSWORD']

fail SendgridToolkit::NoAPIUserSpecified if @api_user.nil? || @api_user.length == 0
fail SendgridToolkit::NoAPIKeySpecified if @api_key.nil? || @api_key.length == 0
end

protected

def api_post(action_name, options = {})
response = HTTParty.post("#{BASE_URI}/#{action_name}",
body: options.to_json, format: :json,
headers: { 'Authorization' => "Basic #{credentials}" })
check_response(response)
end

def api_get(action_name, options = {})
response = HTTParty.get("#{BASE_URI}/#{action_name}",
query: options, format: :json, headers: { 'Authorization' => "Basic #{credentials}" })
check_response(response)
end

def api_delete(action_name, options = {})
response = HTTParty.delete("#{BASE_URI}/#{action_name}",
body: options, format: :json,
headers: { 'Authorization' => "Basic #{credentials}" })
check_response(response)
end

private

def check_response(response)
if response.code > 401
fail(SendgridToolkit::SendgridServerError, "The SendGrid server returned an error. #{response.inspect}")
elsif error?(response) && response['error'].respond_to?(:has_key?) &&
response['error'].key?('code') &&
response['error']['code'].to_i == 401
fail SendgridToolkit::AuthenticationFailed
elsif error?(response)
fail(SendgridToolkit::APIError, response['error'])
end
response
end

def credentials
Base64.encode64("#{@api_user}:#{@api_key}")
end

def error?(response)
response.is_a?(Hash) && response.key?('error')
end
end
end
end
26 changes: 26 additions & 0 deletions lib/sendgrid_toolkit/v3/groups.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module SendgridToolkit
module V3
class Groups < SendgridToolkit::V3::AbstractSendgridClient
def add(options = {})
response = api_post('groups', options)
fail GroupsError if response.is_a?(Hash) && response.key?('errors')
response
end

def get(group_id = nil)
action_name = 'groups' + (group_id ? "/#{group_id}" : '')
response = api_get(action_name)
fail GroupsError if response.is_a?(Hash) && response.key?('errors')
response
end

def delete(options = {})
fail NoGroupIdSpecified unless options[:group_id]

response = api_delete("groups/#{options[:group_id]}")
fail GroupsError if response.is_a?(Hash) && response.key?('errors')
response
end
end
end
end
28 changes: 28 additions & 0 deletions lib/sendgrid_toolkit/v3/unsubscribes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module SendgridToolkit
module V3
class Unsubscribes < SendgridToolkit::V3::AbstractSendgridClient
def add(options = {})
fail NoGroupIdSpecified unless options[:group_id]

response = api_post("groups/#{options[:group_id]}/suppressions", options)
fail APIError if response.is_a?(Hash) && response.key?('errors')
response
end

def delete(options = {})
fail NoGroupIdSpecified unless options[:group_id]

response = api_delete("groups/#{options[:group_id]}/suppressions/#{options[:email]}", options)
fail APIError if response.is_a?(Hash) && response.key?('errors')
response
end

def retrieve(options = {})
fail NoGroupIdSpecified unless options[:group_id]

response = api_get("groups/#{options[:group_id]}/suppressions", options)
response
end
end
end
end
Binary file added sendgrid_toolkit-1.4.0.gem
Binary file not shown.
8 changes: 6 additions & 2 deletions sendgrid_toolkit.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ Gem::Specification.new do |s|
"lib/sendgrid_toolkit/spam_reports.rb",
"lib/sendgrid_toolkit/statistics.rb",
"lib/sendgrid_toolkit/unsubscribes.rb",
'lib/sendgrid_toolkit/v3/groups.rb',
'lib/sendgrid_toolkit/v3/unsubscribes.rb',
'lib/sendgrid_toolkit/v3/abstract_sendgrid_client.rb',
"sendgrid_toolkit.gemspec",
"spec/helper.rb",
"spec/lib/sendgrid_toolkit/abstract_sendgrid_client_spec.rb",
Expand All @@ -53,7 +56,9 @@ Gem::Specification.new do |s|
"spec/lib/sendgrid_toolkit/statistics_spec.rb",
"spec/lib/sendgrid_toolkit/unsubscribes_spec.rb",
"spec/lib/sendgrid_toolkit_spec.rb",
"spec/webconnect/sendgrid_toolkit_spec.rb"
"spec/webconnect/sendgrid_toolkit_spec.rb",
'spec/lib/sendgrid_toolkit/v3/abstract_sendgrid_client_spec.rb',
'spec/lib/sendgrid_toolkit/v3/unsubscribes_spec.rb'
]
s.homepage = "http://github.com/freerobby/sendgrid_toolkit"
s.licenses = ["MIT"]
Expand Down Expand Up @@ -82,4 +87,3 @@ Gem::Specification.new do |s|
s.add_dependency(%q<rspec>, ["~> 2.7.0"])
end
end

3 changes: 2 additions & 1 deletion spec/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
FakeWeb.allow_net_connect = false

REGEX_ESCAPED_BASE_URI = "api\.sendgrid\.com/api"
REGEX_ESCAPED_BASE_URI_V3 = 'api.sendgrid.com/v3/asm'

def backup_env
@env_backup = Hash.new
ENV.keys.each {|key| @env_backup[key] = ENV[key]}
end
def restore_env
@env_backup.keys.each {|key| ENV[key] = @env_backup[key]}
end
end
10 changes: 5 additions & 5 deletions spec/lib/sendgrid_toolkit/statistics_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
FakeWeb.clean_registry
@obj = SendgridToolkit::Statistics.new("fakeuser", "fakepass")
end

describe "#retrieve" do
it "parses daily totals" do
FakeWeb.register_uri(:post, %r|https://#{REGEX_ESCAPED_BASE_URI}/stats\.get\.json\?|, :body => '[{"date":"2009-06-20","requests":12342,"bounces":12,"clicks":10223,"opens":9992,"spamreports":5},{"date":"2009-06-21","requests":32342,"bounces":10,"clicks":14323,"opens":10995,"spamreports":7},{"date":"2009-06-22","requests":52342,"bounces":11,"clicks":19223,"opens":12992,"spamreports":2}]')
Expand All @@ -18,7 +18,7 @@
end
end
end

describe "#retrieve_aggregate" do
it "parses aggregate statistics" do
FakeWeb.register_uri(:post, %r|https://#{REGEX_ESCAPED_BASE_URI}/stats\.get\.json\?.*aggregate=1|, :body => '{"requests":12342,"bounces":12,"clicks":10223,"opens":9992,"spamreports":5}')
Expand All @@ -27,7 +27,7 @@
stats[int].kind_of?(Integer).should == true if stats.has_key?(int) # We support all fields presently returned, but we are only testing what sendgrid officially documents
end
end

it "parses aggregate statistics for array response" do
FakeWeb.register_uri(:post, %r|https://#{REGEX_ESCAPED_BASE_URI}/stats\.get\.json\?.*aggregate=1|, :body => '[{"requests":12342,"bounces":12,"clicks":10223,"opens":9992,"spamreports":5},{"requests":5,"bounces":10,"clicks":10223,"opens":9992,"spamreports":5}]')
stats = @obj.retrieve_aggregate
Expand All @@ -38,7 +38,7 @@
end
end
end

describe "#list_categories" do
it "parses categories list" do
FakeWeb.register_uri(:post, %r|https://#{REGEX_ESCAPED_BASE_URI}/stats\.get\.json\?.*list=true|, :body => '[{"category":"categoryA"},{"category":"categoryB"},{"category":"categoryC"}]')
Expand Down Expand Up @@ -116,4 +116,4 @@
end
end
end
end
end
84 changes: 84 additions & 0 deletions spec/lib/sendgrid_toolkit/v3/abstract_sendgrid_client_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
require File.expand_path("#{File.dirname(__FILE__)}/../../../helper")

describe SendgridToolkit::V3::AbstractSendgridClient do
before do
backup_env
end
after do
restore_env
end

describe '#api_post' do
it "throws error when authentication fails" do
FakeWeb.register_uri(:post, %r|https://#{REGEX_ESCAPED_BASE_URI_V3}/profile\.get\.json\?|, :body => '{"error":{"code":401,"message":"Permission denied, wrong credentials"}}')
@obj = SendgridToolkit::AbstractSendgridClient.new("fakeuser", "fakepass")
lambda {
@obj.send(:api_post, "profile", "get", {})
}.should raise_error SendgridToolkit::AuthenticationFailed
end
it "thows error when sendgrid response is a server error" do
FakeWeb.register_uri(:post, %r|https://#{REGEX_ESCAPED_BASE_URI_V3}/profile\.get\.json\?|, :body => '{}', :status => ['500', 'Internal Server Error'])
@obj = SendgridToolkit::AbstractSendgridClient.new("someuser", "somepass")
lambda {
@obj.send(:api_post, "profile", "get", {})
}.should raise_error SendgridToolkit::AuthenticationFailed
end
it "thows error when sendgrid response is an API error" do
FakeWeb.register_uri(:post, %r|https://#{REGEX_ESCAPED_BASE_URI_V3}/stats\.get\.json\?|, :body => '{"error": "error in end_date: end date is in the future"}', :status => ['400', 'Bad Request'])
@obj = SendgridToolkit::AbstractSendgridClient.new("someuser", "somepass")
lambda {
@obj.send(:api_post, "stats", "get", {})
}.should raise_error SendgridToolkit::SendgridServerError
end
end

describe "#initialize" do
after(:each) do
SendgridToolkit.api_user = nil
SendgridToolkit.api_key = nil
end
it 'stores api credentials when passed in' do
ENV['SMTP_USERNAME'] = 'env_username'
ENV['SMTP_PASSWORD'] = 'env_apikey'

@obj = SendgridToolkit::V3::AbstractSendgridClient.new('username', 'apikey')
@obj.instance_variable_get('@api_user').should == 'username'
@obj.instance_variable_get('@api_key').should == 'apikey'
end
it 'uses module level user and key if they are set' do
SendgridToolkit.api_user = 'username'
SendgridToolkit.api_key = 'apikey'

SendgridToolkit.api_key.should == 'apikey'
SendgridToolkit.api_user.should == 'username'

@obj = SendgridToolkit::V3::AbstractSendgridClient.new
@obj.instance_variable_get('@api_user').should == 'username'
@obj.instance_variable_get('@api_key').should == 'apikey'
end
it 'resorts to environment variables when no credentials specified' do
ENV['SMTP_USERNAME'] = 'env_username'
ENV['SMTP_PASSWORD'] = 'env_apikey'

@obj = SendgridToolkit::V3::AbstractSendgridClient.new()
@obj.instance_variable_get('@api_user').should == 'env_username'
@obj.instance_variable_get('@api_key').should == 'env_apikey'
end
it 'throws error when no credentials are found' do
ENV['SMTP_USERNAME'] = nil
ENV['SMTP_PASSWORD'] = nil

lambda {
@obj = SendgridToolkit::V3::AbstractSendgridClient.new()
}.should raise_error SendgridToolkit::NoAPIUserSpecified

lambda {
@obj = SendgridToolkit::V3::AbstractSendgridClient.new(nil, 'password')
}.should raise_error SendgridToolkit::NoAPIUserSpecified

lambda {
@obj = SendgridToolkit::V3::AbstractSendgridClient.new('user', nil)
}.should raise_error SendgridToolkit::NoAPIKeySpecified
end
end
end
Loading

0 comments on commit 9660316

Please sign in to comment.