Giter VIP home page Giter VIP logo

createsend-ruby's Introduction

createsend Build Status

A Ruby library which implements the complete functionality of the Campaign Monitor API. Requires Ruby >= 2.0.0.

Quick start

Add the gem to your Gemfile:

gem 'createsend'

Or, install the gem:

gem install createsend

Authenticating

The Campaign Monitor API supports authentication using either OAuth or an API key.

Using OAuth

If you're developing a Rails or Rack based application, we recommend using omniauth-createsend to authenticate with the Campaign Monitor API. You might find this example application helpful.

If you don't use omniauth-createsend, you'll need to get access tokens for your users by following the instructions included in the Campaign Monitor API documentation. This gem provides functionality to help you do this, as described below. There's also another example application you may wish to reference, which doesn't depend on any OAuth libraries.

Redirecting to the authorization URL

The first thing your application should do is redirect your user to the Campaign Monitor authorization URL where they will have the opportunity to approve your application to access their Campaign Monitor account. You can get this authorization URL by using CreateSend::CreateSend.authorize_url, like so:

require 'createsend'

authorize_url = CreateSend::CreateSend.authorize_url(
  'Client ID for your application',
  'Redirect URI for your application',
  'The permission level your application requires',
  'Optional state data to be included'
)
# Redirect your users to authorize_url.

Exchanging an OAuth code for an access token

If your user approves your application, they will then be redirected to the redirect_uri you specified, which will include a code parameter, and optionally a state parameter in the query string. Your application should implement a handler which can exchange the code passed to it for an access token, using CreateSend::CreateSend.exchange_token like so:

require 'createsend'

access_token, expires_in, refresh_token = CreateSend::CreateSend.exchange_token(
  'Client ID for your application',
  'Client Secret for your application',
  'Redirect URI for your application',
  'A unique code for your user' # Get the code parameter from the query string
)
# Save access_token, expires_in, and refresh_token.

At this point you have an access token and refresh token for your user which you should store somewhere convenient so that your application can look up these values when your user wants to make future Campaign Monitor API calls.

Making API calls using an access token

Once you have an access token and refresh token for your user, you can use the createsend gem to authenticate and make further API calls like so:

require 'createsend'

auth = {
  :access_token => 'your access token',
  :refresh_token => 'your refresh token'
}
cs = CreateSend::CreateSend.new auth
clients = cs.clients

Refreshing access tokens

All OAuth access tokens have an expiry time, and can be renewed with a refresh token. If your access token expires when attempting to make an API call, the CreateSend::ExpiredOAuthToken exception will be raised, so your code should handle this. You can handle this using either CreateSend::CreateSend.refresh_access_token (when calling class methods) or CreateSend::CreateSend#refresh_token (when calling instance methods).

Here's an example of using CreateSend::CreateSend#refresh_token to refresh your current access token when calling CreateSend::CreateSend#clients:

require 'createsend'

auth = {
  :access_token => 'your access token',
  :refresh_token => 'your refresh token'
}
cs = CreateSend::CreateSend.new auth

begin
  tries ||= 2
  clients = cs.clients
  rescue CreateSend::ExpiredOAuthToken => eot
    access_token, expires_in, refresh_token = cs.refresh_token
    # Here you should save your updated access token, 'expire in' value,
    # and refresh token. `cs` will automatically have the new access token
    # set, so there is no need to set it again.
    retry unless (tries -= 1).zero?
    p "Error: #{eot}"
  rescue Exception => e
    p "Error: #{e}"
end

In addition to raising CreateSend::ExpiredOAuthToken when an access token has expired, this library also raises CreateSend::InvalidOAuthToken if an invalid access token is used, and raises CreateSend::RevokedOAuthToken if a user has revoked the access token being used. This makes it easier for you to handle these cases in your code.

Using an API key

require 'createsend'

cs = CreateSend::CreateSend.new :api_key => 'your api key'
clients = cs.clients

Basic usage

This example of listing all your clients and their campaigns demonstrates basic usage of the library and the data returned from the API:

require 'createsend'

auth = {
  :access_token => 'your access token',
  :refresh_token => 'your refresh token'
}
cs = CreateSend::CreateSend.new auth

clients = cs.clients
clients.each do |cl|
  p "Client: #{cl.Name}"
  client = CreateSend::Client.new auth, cl.ClientID
  p "- Campaigns:"
  client.drafts.each do |cm|
    p "  - #{cm.Subject}"
  end
end

Running this example will result in something like:

Client: First Client
- Campaigns:
  - Newsletter Number One
  - Newsletter Number Two
Client: Second Client
- Campaigns:
  - News for January 2013

Handling errors

If the Campaign Monitor API returns an error, an exception will be raised. For example, if you attempt to create a campaign and enter empty values for subject and other required fields:

require 'createsend'

auth = {
  :access_token => 'your access token',
  :refresh_token => 'your refresh token'
}

begin
  id = CreateSend::Campaign.create auth, "4a397ccaaa55eb4e6aa1221e1e2d7122",
    "", "", "", "", "", "", "", [], []
  p "New campaign ID: #{id}"
  rescue CreateSend::BadRequest => br
    p "Bad request error: #{br}"
    p "Error Code:    #{br.data.Code}"
    p "Error Message: #{br.data.Message}"
  rescue Exception => e
    p "Error: #{e}"
end

Running this example will result in:

Bad request error: The CreateSend API responded with the following error - 304: Campaign Subject Required
Error Code:    304
Error Message: Campaign Subject Required

Expected input and output

The best way of finding out the expected input and output of a particular method in a particular class is to use the unit tests as a reference.

For example, if you wanted to find out how to call the CreateSend::Subscriber.add method, you would look at the file test/subscriber_test.rb

should "add a subscriber with custom fields" do
  stub_post(@auth, "subscribers/#{@list_id}.json", "add_subscriber.json")
  custom_fields = [ { :Key => 'website', :Value => 'http://example.com/' } ]
  email_address = CreateSend::Subscriber.add @auth, @list_id, "[email protected]", "Subscriber", custom_fields, true, "Yes"
  email_address.should be == "[email protected]"
end

Documentation

Ruby documentation is available at RubyDoc.info.

Contributing

Please check the guidelines for contributing to this repository.

Releasing

Please check the instructions for releasing the createsend gem.

This stuff should be green

Build Status Coverage Status Dependency Status Gem Version

createsend-ruby's People

Contributors

27red avatar artempartos avatar cmtiml avatar henrys-cm avatar jacobbednarz avatar jdennes avatar joealba avatar joshgoebel avatar konami99 avatar markstaples avatar matyi avatar mlangsworth avatar niawahyuni avatar pfeiffer avatar philoye avatar rafbm avatar rymai avatar timcraft avatar tinogomes avatar tobio avatar tusharm avatar yahyaz avatar yob avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

createsend-ruby's Issues

I'm getting: MultiJson::DecodeError: 705: unexpected token at

s = c.create_subscriber("[email protected]","1234-5678")
MultiJson::DecodeError: 705: unexpected token at '"[email protected]"'
from /Users/guilherme/.rvm/gems/ree-1.8.7-2012.02@gemset/gems/json-1.4.6/lib/json/common.rb:146:in parse' from /Users/guilherme/.rvm/gems/ree-1.8.7-2012.02@gemset/gems/json-1.4.6/lib/json/common.rb:146:inparse'
from /Users/guilherme/.rvm/gems/ree-1.8.7-2012.02@gemset/gems/multi_json-1.3.6/lib/multi_json/adapters/json_common.rb:7:in load' from /Users/guilherme/.rvm/gems/ree-1.8.7-2012.02@gemset/gems/multi_json-1.3.6/lib/multi_json.rb:93:inload'
from /Users/guilherme/.rvm/gems/ree-1.8.7-2012.02@gemset/gems/httparty-0.11.0/lib/httparty/parser.rb:115:in json' from /Users/guilherme/.rvm/gems/ree-1.8.7-2012.02@gemset/gems/httparty-0.11.0/lib/httparty/parser.rb:134:insend'
from /Users/guilherme/.rvm/gems/ree-1.8.7-2012.02@gemset/gems/httparty-0.11.0/lib/httparty/parser.rb:134:in parse_supported_format' from /Users/guilherme/.rvm/gems/ree-1.8.7-2012.02@gemset/gems/httparty-0.11.0/lib/httparty/parser.rb:100:inparse'
from /Users/guilherme/.rvm/gems/ree-1.8.7-2012.02@gemset/gems/httparty-0.11.0/lib/httparty/parser.rb:64:in call' from /Users/guilherme/.rvm/gems/ree-1.8.7-2012.02@gemset/gems/httparty-0.11.0/lib/httparty/request.rb:215:inparse_response'
from /Users/guilherme/.rvm/gems/ree-1.8.7-2012.02@gemset/gems/httparty-0.11.0/lib/httparty/request.rb:185:in handle_response' from /Users/guilherme/.rvm/gems/ree-1.8.7-2012.02@gemset/gems/httparty-0.11.0/lib/httparty/response.rb:18:incall'
from /Users/guilherme/.rvm/gems/ree-1.8.7-2012.02@gemset/gems/httparty-0.11.0/lib/httparty/response.rb:18:in parsed_response' from /Users/guilherme/.rvm/gems/ree-1.8.7-2012.02@gemset/gems/createsend-3.3.0/lib/createsend/subscriber.rb:33:inadd'
from /Users/guilherme/Development/x/y/app/models/y.rb:22:in `create_subscriber'
from (irb):13

The auth data is ok. The subscriber is added but the response is bugged :( what to do ?

Issues with UTF-8 encoding in emails

Hi,

Reading the source and the CampaignMonitor documentation it's quite clear that it accepts UTF-8 encoded data. However, the following script does not work with createsend-ruby:

#encoding=utf-8
require 'createsend'
CreateSend.api_key '<snip>'

puts CreateSend::VERSION

CreateSend::Subscriber.add('<snip>', "jøsses@bådbasæn.dk", "Tester", [], false)

Trace:

1.1.1
/Users/sirup/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/createsend-1.1.1/lib/createsend.rb:141:in `handle_response': The CreateSend API responded with the following error - 1: Please provide a valid email address. (CreateSend::BadRequest)
        from /Users/sirup/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/createsend-1.1.1/lib/createsend.rb:134:in `post'
        from /Users/sirup/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/createsend-1.1.1/lib/createsend/subscriber.rb:30:in `add'
        from hej.rb:7:in `<main>'

It works fine with special characters in the name. I wrote to CampaignMonitor support as well to ask finally if they accept special characters in email (as they should).

Crack::ParseError

Hi,

I'm trying to add campaign monitor to our project. I'm using this gem to access campaign monitor.

Here is what i'm doing:

CreateSend.api_key my_api_key
cs = CreateSend::CreateSend.new

list = CreateSend::List.new my_list_id
date = Date.new(2001,3,1).strftime("%Y-%m-%d")
list.active(date)

And I got the following error:
Crack::ParseError: Invalid JSON string
from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/crack-0.1.8/lib/crack/json.rb:14:in rescue in parse' from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/crack-0.1.8/lib/crack/json.rb:12:inparse'
from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/httparty-0.7.8/lib/httparty/parser.rb:116:in json' from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/httparty-0.7.8/lib/httparty/parser.rb:136:inparse_supported_format'
from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/httparty-0.7.8/lib/httparty/parser.rb:103:in parse' from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/httparty-0.7.8/lib/httparty/parser.rb:66:incall'
from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/httparty-0.7.8/lib/httparty/request.rb:217:in parse_response' from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/httparty-0.7.8/lib/httparty/request.rb:189:inhandle_response'
from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/httparty-0.7.8/lib/httparty/request.rb:71:in perform' from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/httparty-0.7.8/lib/httparty.rb:390:inperform_request'
from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/httparty-0.7.8/lib/httparty.rb:342:in get' from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/createsend-0.3.2/lib/createsend.rb:109:inget'
from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/createsend-0.3.2/lib/createsend/list.rb:173:in get' from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/createsend-0.3.2/lib/createsend/list.rb:86:inactive'
from (irb):9
from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/railties-3.0.9/lib/rails/commands/console.rb:44:in start' from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/railties-3.0.9/lib/rails/commands/console.rb:8:instart'
from /Users/migoo/Documents/Work/webdoc/lock/ruby/1.9.1/gems/railties-3.0.9/lib/rails/commands.rb:23:in <top (required)>' from script/rails:6:inrequire'
from script/rails:6:in `

'

I try to change the date format, but it change nothing.
Did I missed something or there is a bug somewhere ?

Thanks for help.
Migoo

Having an issue with creating client

Have just been trying things out in the Ruby console:

CreateSend.api_key my_api_key_here
CreateSend::Client.create("my Store Name", "(GMT-08:00) Pacific Time (US & Canada)", "Canada")

I get:

CreateSend::BadRequest: The CreateSend API responded with the following error - 400: Failed to deserialize your request. 
Please check the documentation and try again.
Fields in error: client
    from /Users/tammam/.rvm/gems/ruby-1.9.3-p362/gems/createsend-2.5.0/lib/createsend.rb:154:in `handle_response'
    from /Users/tammam/.rvm/gems/ruby-1.9.3-p362/gems/createsend-2.5.0/lib/createsend.rb:147:in `post'
    from /Users/tammam/.rvm/gems/ruby-1.9.3-p362/gems/createsend-2.5.0/lib/createsend/client.rb:19:in `create'

It's not an issue with my API key because the basic example works:

CreateSend.api_key 'your_api_key'
cs = CreateSend::CreateSend.new

Any ideas?

API Segment Rule Docs Missing

Maybe I'm missing them, but I cannot find docs for the data-type-specific verbs allowed for segment rules.

I expected to find them here: https://www.campaignmonitor.com/api/segments/#creating-a-segment

I do see a list buried in this help article: https://help.campaignmonitor.com/topic.aspx?t=90

...but when I try the ones listed there (eg the verb "MATCHES EXACTLY" for a MultiSelectOne), I get an error. It turns out that although the help article linked above says "matches exactly", the API expects "EQUALS".

Docs listing all the verbs allowed for each data type would be very helpful.

Thanks!

Creating segments gets error 2700 "Subject cannot be empty"

Trying to create some custom segments via api but getting stuck

Tried the code from https://github.com/campaignmonitor/createsend-ruby/blob/master/test/segment_test.rb like

auth = {:api_key => "FOO"}
list_id = "BAR"
rule_groups = [ { :Rules => [ { :Subject => "EmailAddress", :Clause => "CONTAINS example.com" } ] } ]
CreateSend::Segment.create auth, list_id, "Users signed up via test", rule_groups

Get error with code 2700 and message Subject cannot be empty via the ResultData

Looking on another segment created (via website) via API states the subject parameter. But looking at http://www.campaignmonitor.com/api/segments/#creating_a_segment it says I'm suppose to send RuleType?

What I'm I doing wrong?

Unable to add subscribers

I'm getting a :

MultiJson::DecodeError at / 743: unexpected token at '"[email protected]"'

When using the CreateSend::Subscriber.add method the API returns "[email protected]", which is not valid JSON.

Some suggestions if I may be so bold:

  • Always return valid JSON from the API
  • Handle JSON parse issues more gracefully

Custom fields?

Hi,

Just trying to understand how custom fields work? I tried the following command

email = CreateSend::Subscriber.add 'c82b5336917ecfa9fbd8f6d1496f4bcd', "[email protected]", "Daniel", [{ :Key => 'website', :Value => 'http://example.com/' }], true

where the custom fields are taken directly from one of the unit tests.

When I later retrieve the entry the custom fields are empty.

ruby-1.9.2-p0 :006 > email = CreateSend::Subscriber.get 'c82b5336917ecfa9fbd8f6d1496f4bcd', "[email protected]" => <#Hashie::Mash CustomFields=[] Date="2011-01-26 14:11:00" EmailAddress="[email protected]" Name="Daniel" State="Active">

Am I doing something wrong or is there an issue with the custom fields?

Regards,

  • Daniel

Subscriber custom fields api sucks!

Ok, ok, OKAY!

Sucks is a strong word… but I spent some time checking out your api docs, source code and tests before I realised the object format for sending custom fields to your subscriber API.

I ended up implementing using something like this:

custom_fields = user_data.inject([]) do |fields, data|
    fields << { :Key => data.first, :Value => data.last }
end

So I thought I'd share the snippet and strongly suggest that you either document it or make it so I can just throw a hash to the Subscriber.add method.

Cheers,

Ben

Subscriber.import args

What's the correct way to build a list of subscribers for Subscriber.import? I would expect there to be an initializer for Subscriber along the lines of:

Subscriber.new(email_address, name, custom_fields, resubscribe, consent_to_track, restart_subscription_based_autoresponders)

... which I would then pass along to Subscriber.import.

The only initializer for Subscriber doesn't take most of thost args (and I'm not sure how to set them after-the-fact).

All API calls failing due to JSON parse errors

As of today, all API calls using this gem started to fail due to JSON parse errors which were caused by the UTF8 BOM which you apparently have added to every response.

I worked around this by patching the gem / stripping the BOM out in case the initial parsing fails.
If this is an intended / permanent change to your API I could provide a proper fix / pull request.

Cheers,
Jens

[enhancement] send the last campaign to new subscribers

We send campaigns weekly and for new subscribers, they should receive the last campaign. I looked in the auto-responder section and noticed while is the option to create a standard campaign that gets sent, there isn't an option to send the last campaign.

Seems like this option would be super awesome for your customers who send campaigns on a schedule (e.g. weekly, monthly).

Do you think this could be a Campaign Monitor offering?

User-Agent header includes HTTParty version rather than CreateSend version

The User-Agent header in calls made by this library includes the VERSION constant from the included HTTParty module rather than the VERSION constant from the CreateSend module itself.

So, currently the user agent should actually be createsend-ruby-1.0.2 rather than createsend-ruby-0.8.1 (for example).

API gives incorrect/delayed results

I'm getting an error when querying properties of recently created lists: After I create a list, I then query for the list of lists (CreateSend::Client#lists) and the recently-created list is missing most of the time.

This rake task consistently exhibits the issue for me:

require 'createsend'

namespace :campaign_monitor do
  task :demo do
    auth = {api_key: API_KEY}
    client_id = CreateSend::CreateSend.new(auth).clients.tap do |cs|
      raise if cs.size != 1
    end.first['ClientID']

    client = CreateSend::Client.new(auth, client_id)

    p [:available_lists, client.lists.map(&:Name)]
    # (empty list to start)

    20.times do |i|
      puts "Creating list#{i}"
      list_id = CreateSend::List.create(auth, client_id, "list#{i}", "https://localhost", false, "https://localhost")

      sleep 10 

      p [:available_lists, client.lists.map(&:Name)]
      # Even after 10 seconds, still missing....

      puts
    end
  end
end

Output:

[:available_lists, []]
Creating list0
[:available_lists, []]

Creating list1
[:available_lists, []]

Creating list2
[:available_lists, ["list0", "list1", "list2"]]

Creating list3
[:available_lists, ["list0", "list1", "list2"]]

Creating list4
[:available_lists, ["list0", "list1", "list2"]]

Creating list5
[:available_lists, ["list0", "list1", "list2", "list3", "list4", "list5"]]

Creating list6
[:available_lists, ["list0", "list1", "list2", "list3", "list4", "list5"]]

Creating list7
[:available_lists, ["list0", "list1", "list2", "list3", "list4", "list5"]]

.... (etc, same 30-second delay for the rest) ...

I can, however, grab the list ID that comes back from the CreateSend::List.create call, and use it to delete the list from the server (presumably I could do other things with it via the API).

This behavior definitely feels unintuitive, so some docs about what my expectations should be would be nice at least.

Fails to deserialize request

Hello,

When I try to add a subscriber with the following name: "Test\u{ffa1}"
the request returns a 400 error. This is happening in production and when testing it out in a repl. When I check the encoding of the string, it is set to UTF8.

I tried adding "# encoding: utf-8" to the top of all of your wrapper files, but that didn't fix it. I also emailed the Campaign Monitor support team and they suggested I specify the file's encoding and to also open an issue here.

Any idea why this might be happening? I just noticed a lot of errors a few days ago when we had a bunch of users from Quebec sign up. They have accents in their name and that is triggering the error.

Thanks

1000 limit for API responses

I'm running API calls and only getting the first 1000 results returned. Is there a way to get all results, or at least pass in a page number?

# List has 1300 active subscribers
list = CreateSend::List.new CAMPAIGN_MONITOR_AUTH, intro_list_id
# The following returns:
#1000
puts list.active['Results'].count

SegmentIDs ignored when creating a new Campaign

Howdy!

I'm trying to send two emails via the API, one with a day theme and one with a night theme. I have the appropriate segments set up on my list, but when I make the API call it ignores the SegmentIDs and sends to the entire list. Here is the JSON payload that is being sent:

{
 "Subject": "The Hottest Repos on GitHub - Mar 08",
 "Name": "Nightly – Mar 08 (day theme)",
 "FromName": "Changelog Nightly",
 "FromEmail": "[email protected]",
 "ReplyTo": "[email protected]",
 "HtmlUrl": "http://nightly.thechangelog.com/2015/03/08/email-day.html",
 "TextUrl": null,
 "ListIDs": ["1cf7c272705e97f1be3d49acd2d82cd8"],
 "SegmentIDs": ["26098f2c17ae0f62acb49690785477e1”]
}

You can see the exact code I'm using to generate this payload in our repo, right here.

Any idea why the segments aren't being picked up? Thanks!

Opt-in type

Is it possible to set confirmation type via this gem?

Single Opt-in (no confirmation) vs Confirmed Opt-in (with email confirmation).

I know there is option on your website but it would be great to set it over API.

I use this method to add email subscriber:
CreateSend::Subscriber.add @auth, @list_id, email, "Subscriber", nil, true

CreateSend::ServerError

Odd, I'm doing a simple CreateSend::Subscriber.add and on occasion this exception is thrown.

Anyone have an idea on how I can get a more meaningful error out of the gem, or seen this before?

Is this a simple timeout I can address (or the gem can address via a retry), or is there a more fundamental reason for this exception? While this is only appearing in the dev environment now, I'm concerned about recovering from it in production.

Thoughts?

Namespaced?

Right now it looks like the classes are in the global namespace? That would be a problem for apps like mine that have a List model. Namespacing everything under a CreateSend module or some such would be really good for compatibility.

Authentication using API key only isn't working

I'm following this very basic example using the API key I retrieved from my account settings.

require 'createsend'
cs = CreateSend::CreateSend.new :api_key => 'your api key'
clients = cs.clients

And it always results in this error:
CreateSend::Unauthorized: The CreateSend API responded with the following error - 50: Must supply a valid HTTP Basic Authorization header

Does the API no longer support key based authentication?

GZip deflate causing JSON parse error when using gem

To reproduce

  1. Have a list containing 8 SmartMail templates (this is over 1KB of content for us)
  2. Call this method
      CreateSend::Transactional::SmartEmail.list(
        { api_key: Rails.application.secrets.campaignmonitor_api_key },
        clientID: Rails.application.secrets.campaignmonitor_client_id
      )

This crashes with

JSON::ParserError: 765: unexpected token at '���'
…d/bundle/ruby/2.3.0/gems/json-2.1.0/lib/json/common.rb:  156:in `parse'
…d/bundle/ruby/2.3.0/gems/json-2.1.0/lib/json/common.rb:  156:in `parse'
…ruby/2.3.0/gems/httparty-0.15.6/lib/httparty/parser.rb:  123:in `json'
…ruby/2.3.0/gems/httparty-0.15.6/lib/httparty/parser.rb:  143:in `parse_supported_format'
…ruby/2.3.0/gems/httparty-0.15.6/lib/httparty/parser.rb:  108:in `parse'
…ruby/2.3.0/gems/httparty-0.15.6/lib/httparty/parser.rb:   67:in `call'
…uby/2.3.0/gems/httparty-0.15.6/lib/httparty/request.rb:  382:in `parse_response'
…uby/2.3.0/gems/httparty-0.15.6/lib/httparty/request.rb:  350:in `block in handle_response'
…by/2.3.0/gems/httparty-0.15.6/lib/httparty/response.rb:   25:in `parsed_response'
…by/2.3.0/gems/httparty-0.15.6/lib/httparty/response.rb:   88:in `method_missing'
…send-4.1.1/lib/createsend/transactional_smart_email.rb:    9:in `list'
…1109093951/app/controllers/my_controller.rb:   63:in `block in get_campaign_monitor_autoresponders_list'

We have investigated this and the below HTTParty call is what the gem is roughly doing based on this line:
https://github.com/campaignmonitor/createsend-ruby/blob/master/lib/createsend/createsend.rb#L220
And the line that sets the header is: https://github.com/campaignmonitor/createsend-ruby/blob/master/lib/createsend/createsend.rb#L118

r = HTTParty.get("https://api.createsend.com/api/v3.1/transactional/smartemail", {headers: { "Accept-Encoding" => "gzip, deflate"}, query: options, basic_auth: { username: Rails.application.secrets.campaignmonitor_api_key, password: 'x'}})
r.body => #Still GZipped content. JSON cannot parse this!

Expected:

r = HTTParty.get("https://api.createsend.com/api/v3.1/transactional/smartemail", {query: options, basic_auth: { username: Rails.application.secrets.campaignmonitor_api_key, password: 'x'}})
r.body => # String encoded JSON that parses just fine!

Get all results instead of paged

It would be really handy to add a method for retrieving all results, rather than just paged. I can work on it and submit a PR if you want.

Here's an example method from lib/createsend/list.rb:

    # Gets paged active subscribers for this list.
    def active(date="", page=1, page_size=1000, order_field="email",
      order_direction="asc")
      paged_result_by_date("active", date, page, page_size, order_field,
        order_direction)
    end

I would add something below it like:

    # Gets all active subscribers for this list.
    def all_active(date="")
      all_results_by_date("active", date)
    end

CreateSend::Subscriber.get doesn't work

Example for generating error:

@auth = {api_key: "myapikey"}
u = User.last
c = CreateSend::Subscriber.get(@auth, AppCampaignMonitor.list, u.email)

Stack Trace

Psych::SyntaxError: (<unknown>): found unexpected ':' while scanning a plain scalar at line 1 column 71

from /opt/ruby-2.0.0-p0/lib/ruby/2.0.0/psych.rb:205:in `parse'
    from /opt/ruby-2.0.0-p0/lib/ruby/2.0.0/psych.rb:205:in `parse_stream'
    from /opt/ruby-2.0.0-p0/lib/ruby/2.0.0/psych.rb:153:in `parse'
    from /opt/ruby-2.0.0-p0/lib/ruby/2.0.0/psych.rb:129:in `load'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/crack-0.3.2/lib/crack/json.rb:12:in `parse'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/tinder-1.4.1/lib/tinder/connection.rb:9:in `json'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/httparty-0.11.0/lib/httparty/parser.rb:134:in `parse_supported_format'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/httparty-0.11.0/lib/httparty/parser.rb:100:in `parse'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/httparty-0.11.0/lib/httparty/parser.rb:64:in `call'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/httparty-0.11.0/lib/httparty/request.rb:215:in `parse_response'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/httparty-0.11.0/lib/httparty/request.rb:185:in `block in handle_response'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/httparty-0.11.0/lib/httparty/response.rb:18:in `call'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/httparty-0.11.0/lib/httparty/response.rb:18:in `parsed_response'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/httparty-0.11.0/lib/httparty/response.rb:56:in `method_missing'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/hashie-2.0.4/lib/hashie/mash.rb:148:in `deep_update'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/hashie-2.0.4/lib/hashie/mash.rb:66:in `initialize'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/createsend-3.2.0/lib/createsend/subscriber.rb:18:in `new'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/createsend-3.2.0/lib/createsend/subscriber.rb:18:in `get'
    from (irb):3
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/railties-3.2.12/lib/rails/commands/console.rb:47:in `start'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/railties-3.2.12/lib/rails/commands/console.rb:8:in `start'
    from /var/www/househappy.org/shared/bundle/ruby/2.0.0/gems/railties-3.2.12/lib/rails/commands.rb:41:in `<top (required)>'
    from script/rails:6:in `require'

Using Ruby 2.0.0-p0

Gemfile

  * actionmailer (3.2.12)
  * actionpack (3.2.12)
  * activeadmin (0.6.0 5f5ab0f)
  * activemodel (3.2.12)
  * activerecord (3.2.12)
  * activerecord-postgis-adapter (0.6.2)
  * activeresource (3.2.12)
  * activesupport (3.2.12)
  * arbre (1.0.1)
  * arel (3.0.2)
  * authlogic (3.3.0 51416fe)
  * awesome_print (1.1.0)
  * backports (2.6.7)
  * bcrypt-ruby (3.0.1)
  * bourbon (3.1.6)
  * browser (0.1.6)
  * bson (1.8.5)
  * bson_ext (1.8.5)
  * builder (3.0.4)
  * bundler (1.3.1)
  * campaign_monitor_subscriber (1.0.3)
  * capistrano (2.15.2)
  * capistrano-campfire (0.2.1)
  * carrierwave (0.8.0)
  * carrierwave-vips (1.0.4 af85aed)
  * celluloid (0.13.0)
  * childprocess (0.3.9)
  * chronic (0.9.1)
  * coderay (1.0.9)
  * coffee-rails (3.2.2)
  * coffee-script (2.2.0)
  * coffee-script-source (1.6.2)
  * connection_pool (1.0.0)
  * crack (0.3.2)
  * createsend (3.2.0)
  * curb (0.8.3)
  * curb-fu (0.6.2)
  * dalli (2.6.2)
  * descendants_tracker (0.0.1)
  * devise (2.2.3)
  * diff-lcs (1.2.4)
  * domain_name (0.5.11)
  * erubis (2.7.0)
  * eventmachine (1.0.3)
  * excon (0.20.1)
  * execjs (1.4.0)
  * factory_girl (4.2.0)
  * factory_girl_rails (4.2.1)
  * faker (1.1.2)
  * faraday (0.8.7)
  * fastercsv (1.5.5)
  * ffi (1.8.1)
  * fog (1.10.1 ca53b99)
  * formatador (0.2.4)
  * formtastic (2.2.1)
  * geocoder (1.1.8)
  * god (0.13.2)
  * guard (1.8.0)
  * guard-spork (1.5.0)
  * handlebars_assets (0.12.0)
  * has_scope (0.5.1)
  * hashie (2.0.4)
  * highline (1.6.18)
  * hike (1.2.2)
  * http_parser.rb (0.5.3)
  * httparty (0.11.0)
  * httpauth (0.2.0)
  * i18n (0.6.4)
  * inherited_resources (1.4.0)
  * journey (1.0.4)
  * jquery-rails (2.2.1)
  * json (1.7.7)
  * jwt (0.1.8)
  * kaminari (0.14.1)
  * kgio (2.8.0)
  * libv8 (3.11.8.17)
  * listen (1.0.3)
  * lumberjack (1.0.3)
  * mail (2.4.4)
  * mail_view (1.0.3)
  * mechanize (2.6.0)
  * meta_search (1.1.3)
  * method_source (0.8.1)
  * mime (0.1)
  * mime-types (1.23)
  * money (5.1.1)
  * multi_json (1.7.3)
  * multi_xml (0.5.3)
  * multipart-post (1.2.0)
  * net-http-digest_auth (1.3)
  * net-http-persistent (2.8)
  * net-scp (1.1.0)
  * net-sftp (2.1.1)
  * net-ssh (2.6.7)
  * net-ssh-gateway (1.2.0)
  * newrelic-carrierwave (0.3.0 60d9bdb)
  * newrelic_rpm (3.6.1.88)
  * nokogiri (1.5.9)
  * ntlm-http (0.1.1)
  * oauth (0.4.7)
  * oauth2 (0.8.1)
  * oj (2.0.12)
  * omniauth (1.1.4)
  * omniauth-facebook (1.4.1)
  * omniauth-google-oauth2 (0.1.17)
  * omniauth-oauth (1.0.1)
  * omniauth-oauth2 (1.1.1)
  * omniauth-twitter (0.0.16)
  * orm_adapter (0.4.0)
  * pg (0.15.1)
  * polyamorous (0.5.0)
  * polyglot (0.3.3)
  * pretender (0.0.3)
  * progressbar (0.20.0)
  * pry (0.9.12.1)
  * rabl (0.8.4)
  * rack (1.4.5)
  * rack-cache (1.2)
  * rack-contrib (1.1.0)
  * rack-protection (1.5.0)
  * rack-ssl (1.3.3)
  * rack-test (0.6.2)
  * rails (3.2.12)
  * railties (3.2.12)
  * raindrops (0.11.0)
  * rake (10.0.4)
  * rb-fsevent (0.9.3)
  * rb-inotify (0.9.0)
  * rb-kqueue (0.2.0)
  * rdoc (3.12.2)
  * redis (3.0.4)
  * redis-namespace (1.2.1)
  * ref (1.0.4)
  * responders (0.9.2)
  * rgeo (0.3.20)
  * rgeo-activerecord (0.5.0)
  * rmagick (2.13.2)
  * rspec (2.13.0)
  * rspec-core (2.13.1)
  * rspec-expectations (2.13.0)
  * rspec-mocks (2.13.1)
  * rspec-rails (2.13.1)
  * ruby-hmac (0.4.0)
  * ruby-vips (0.3.5 6abdf48)
  * rubyzip (0.9.9)
  * rvm-capistrano (1.3.0)
  * sass (3.2.8)
  * sass-rails (3.2.6)
  * selenium-webdriver (2.32.1)
  * sidekiq (2.11.1)
  * simple_oauth (0.1.9)
  * sinatra (1.3.6)
  * sitemap_generator (4.0.1)
  * slim (1.3.8)
  * slop (3.4.4)
  * soulmate (1.0.0)
  * spork (0.9.2)
  * sprockets (2.2.2)
  * state_machine (1.2.0)
  * temple (0.6.4)
  * test-unit (2.5.4)
  * therubyracer (0.11.4)
  * thor (0.18.1)
  * tilt (1.3.7)
  * timers (1.1.0)
  * tinder (1.4.1)
  * treetop (1.4.12)
  * twitter-stream (0.1.16)
  * tzinfo (0.3.37)
  * uglifier (2.0.1)
  * unf (0.1.1)
  * unf_ext (0.0.6)
  * unicorn (4.3.1)
  * vcr (2.4.0)
  * vegas (0.1.11)
  * virtus (0.5.4)
  * warden (1.2.1)
  * watir-webdriver (0.6.3)
  * webrobots (0.1.1)
  * websocket (1.0.7)
  * whenever (0.8.2)
  * wicked_pdf (0.9.6)
  * will_paginate (3.0.4)

As well, it's fixed if after this line (https://github.com/campaignmonitor/createsend-ruby/blob/master/lib/createsend/subscriber.rb#L17) you add:

response = JSON.parse(response.body)

This is definitely killing us in production; not sure why or when this started happening but it's happening across all of our environments (dev, staging, production).

Tangent: When I use my suggestion above, I am unable to unsubscribe users. This gem seems to have gotten a huge rework since I last looked (few months ago)... though I never saw a deprecation warning; am I missing something, crazy, or did you leave them out for some reason?

sending smart transactional email results in CreateSend::ServerError

I'm not sure if this an issue or not. But as the issue says I am trying to send smart transactional emails and can't get past this error

CreateSend::ServerError: CreateSend::ServerError
from /Users/*******/.rvm/gems/ruby-2.2.0/gems/createsend-4.1.0/lib/createsend/createsend.rb:283:in `handle_response'

verbatim, this is the ruby class from which I am trying to make calls to campaign monitor


class Indigo 
  require 'createsend'

  extend Formats

  API_KEY = "*********************"
  BOOKING_CONFIRMATION_ID = "028ec00a-e764-4291-a294-***********"

  def self.booking_confirmation(booking) 
    hashify_and_send_booking(BOOKING_CONFIRMATION_ID, booking) if booking
  end

  def self.hashify_and_send_booking(type, booking)
    smart_transactional_email(type).send(hashify_booking(booking)) 
  end

  private

  def self.hashify_booking(booking)
    h=Hash.new.tap do |ph|
      ph[:To] = [booking.firstname, "<#{booking.email}>"]
      ph[:Data] = Hash.new.tap do |dh|
        dh["firstName"] = booking.firstname
        dh["spaceTitle"] = booking.space_title
        dh["venueTitle"] = booking.venue_title
        dh["bookingDate"] = formatted_datetime booking.booked_from
        dh["bookingId"] = booking.id
      end
    end
    JSON.generate h      
  end

  def self.smart_transactional_email(type)
    CreateSend::Transactional::SmartEmail.new({api_key: API_KEY}, type)
  end

end 

I can't be sure if it an error with createsend or something i am doing wrong

Thread safety and #refresh_token

I realise this is a generic question which could apply to many similar situations but I thought I'd bring it up in case anyone thinks it's useful to mention it in createsend-ruby's README.md.

In multi-threaded applications, it is possible to get into an unsafe situation when refreshing tokens, e.g.:

record = SomeModel.find(...)
cs = CreateSend::CreateSent.new(
    record.access_token,
    record.refresh_token
)

begin
  tries = 2
  cs.clients
rescue CreateSend::ExpiredOAuthToken => e
  access_token, expires_in, request_token = cs.refresh_token
  record.access_token = access_token
  record.request_token = request_token
  record.save
  retry if (trues -= 1).zero?
  raise e
end
  1. record.access_token expires
  2. Thread A encounters ExpiredOAuthToken
  3. Thread A calls #refresh_token
  4. Thread B encounters ExpiredOAuthToken
  5. Thread B calls #refresh_token but fails because record.refresh_token is now invalid
  6. Thread A persists new token
  7. Thread A continues

I'm assuming others have encountered this possibility. Anyone have any favoured suggestions for dealing with it?

I've created a question on SO too (modified the code to make it broader than createsend-ruby) if anyone would like some points!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.