expresspigeon-ruby 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.rbc
2
+ *.sassc
3
+ .sass-cache
4
+ capybara-*.html
5
+ .rspec
6
+ /.bundle
7
+ /vendor/bundle
8
+ /log/*
9
+ /tmp/*
10
+ /db/*.sqlite3
11
+ /public/system/*
12
+ /coverage/
13
+ /spec/tmp/*
14
+ **.orig
15
+ rerun.txt
16
+ pickle-email-*.html
17
+ doc/structures.csv
18
+ *.iml
19
+ .idea/
data/.rvmrc ADDED
@@ -0,0 +1,8 @@
1
+ USER_RVMRC='.rvmrc.local'
2
+
3
+ if [ -f $USER_RVMRC ]; then
4
+ source $USER_RVMRC
5
+ else
6
+ rvm use 1.9.3@expresspigeon-ruby --create
7
+ fi
8
+
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in expresspigeon-ruby.gemspec
4
+ gemspec
5
+
data/Gemfile.lock ADDED
@@ -0,0 +1,24 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ expresspigeon-ruby (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.5)
10
+ rspec (2.14.1)
11
+ rspec-core (~> 2.14.0)
12
+ rspec-expectations (~> 2.14.0)
13
+ rspec-mocks (~> 2.14.0)
14
+ rspec-core (2.14.5)
15
+ rspec-expectations (2.14.3)
16
+ diff-lcs (>= 1.1.3, < 2.0)
17
+ rspec-mocks (2.14.3)
18
+
19
+ PLATFORMS
20
+ ruby
21
+
22
+ DEPENDENCIES
23
+ expresspigeon-ruby!
24
+ rspec (~> 2.6)
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 ExpressPigeon
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Expresspigeon::Ruby
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'expresspigeon-ruby'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install expresspigeon-ruby
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'expresspigeon-ruby/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "expresspigeon-ruby"
8
+ gem.version = Expresspigeon::API::VERSION
9
+ gem.authors = ["ipolevoy"]
10
+ gem.email = ["ipolevoy@groupon.com"]
11
+ gem.description = %q{ExpressPigeon Ruby API for sending transactional messages, manipulating lists, contacts nad more}
12
+ gem.summary = %q{ExpressPigeon API Ruby Wrapper}
13
+ gem.homepage = "https://github.com/expresspigeon/expresspigeon-ruby"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ gem.add_development_dependency "rspec", "~> 2.6"
20
+ end
@@ -0,0 +1,5 @@
1
+ module Expresspigeon
2
+ module API
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,306 @@
1
+ require "expresspigeon-ruby/version"
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'uri'
6
+
7
+ AUTH_KEY = ENV['EXPRESSPIGEON_AUTH_KEY']
8
+ ROOT = 'https://api.expresspigeon.com/'
9
+ #ROOT = 'http://localhost:8888/api/'
10
+ USE_SSL = true
11
+
12
+ module ExpressPigeon
13
+ module API
14
+ unless AUTH_KEY
15
+ raise 'Provide EXPRESSPIGEON_AUTH_KEY environment variable'
16
+ end
17
+
18
+ def http(path, method, params = {})
19
+ uri = URI.parse "#{ROOT}#{path}"
20
+ req = Net::HTTP.const_get("#{method}").new "#{ROOT}#{path}"
21
+ req['X-auth-key'] = AUTH_KEY
22
+ if params
23
+ req.body = params.to_json
24
+ req['Content-type'] = 'application/json'
25
+ end
26
+
27
+ if block_given?
28
+ Net::HTTP.start(uri.host, uri.port, :use_ssl => USE_SSL) do |http|
29
+ http.request req do |res|
30
+ res.read_body do |seg|
31
+ yield seg
32
+ end
33
+ end
34
+ end
35
+ else
36
+ resp = Net::HTTP.start(uri.host, uri.port, :use_ssl => USE_SSL) do |http|
37
+ http.request req
38
+ end
39
+ parsed = JSON.parse(resp.body)
40
+ if parsed.kind_of? Hash
41
+ MetaHash.new parsed
42
+ else
43
+ parsed
44
+ end
45
+ end
46
+ end
47
+
48
+ def get(path, &block)
49
+ http path, 'Get', nil, &block
50
+ end
51
+
52
+ def post(path, params = {})
53
+ http path, 'Post', params
54
+ end
55
+
56
+ def del(path, params = {})
57
+ http path, 'Delete', params
58
+ end
59
+
60
+ def self.campaigns
61
+ Campaigns.new
62
+ end
63
+
64
+ def self.lists
65
+ Lists.new
66
+ end
67
+
68
+ def self.contacts
69
+ Contacts.new
70
+ end
71
+
72
+ def self.messages
73
+ Messages.new
74
+ end
75
+ end
76
+
77
+ end
78
+
79
+ class MetaHash < Hash
80
+
81
+ def initialize(delegate)
82
+ super
83
+ @delegate = delegate
84
+ @delegate.each_key do |k|
85
+ v = @delegate[k] # lets go only one level down for now
86
+ if v.kind_of? Hash
87
+ @delegate[k] = MetaHash.new(v)
88
+ end
89
+ end
90
+ end
91
+
92
+ def method_missing(m, *args, &block)
93
+ @delegate[m.to_s]
94
+ end
95
+
96
+ def to_s
97
+ @delegate.to_s
98
+ end
99
+
100
+ end
101
+
102
+ class Lists
103
+
104
+ include ExpressPigeon::API
105
+
106
+ def initialize
107
+ @endpoint = 'lists'
108
+ end
109
+
110
+ def create(list_name, from_name, reply_to)
111
+ post @endpoint, {:name => list_name, :from_name => from_name, :reply_to => reply_to}
112
+ end
113
+
114
+
115
+ # Query all lists.
116
+ # returns: array of hashes each representing a list for this user
117
+ def all
118
+ get @endpoint
119
+ end
120
+
121
+
122
+ #Updates existing list
123
+ #
124
+ #:param list_id: Id of list to be updated
125
+ #:type list_id: int
126
+ #
127
+ #:param params: JSON object represents a list to be updated
128
+
129
+ #
130
+ #:returns: EpResponse with status, code, message, and updated list
131
+ #:rtype: EpResponse
132
+ #TODO: resolve API on Python side, then implement this
133
+ #def update(list_id, params = {})
134
+ # params['id'] = list_id
135
+ # return self.ep.put(self.endpoint, params=params)
136
+ #end
137
+
138
+
139
+ # Removes a list with a given id. A list must be enabled and has no dependent subscriptions and/or scheduled campaigns.
140
+ #
141
+ # param list_id: Id of list to be removed.
142
+ # returns response hash with status, code, and message
143
+ def delete(list_id)
144
+ del "#{@endpoint}/#{list_id}"
145
+ end
146
+
147
+ def csv(list_id, &block)
148
+ get "#{@endpoint}/#{list_id}/csv", &block
149
+ end
150
+
151
+
152
+ end
153
+
154
+ class Campaigns
155
+ include ExpressPigeon::API
156
+
157
+ def initialize
158
+ @endpoint = 'campaigns'
159
+ end
160
+
161
+ def all
162
+ get @endpoint
163
+ end
164
+
165
+ def report(campaign_id)
166
+ get "#{@endpoint}/#{campaign_id}"
167
+ end
168
+
169
+ def bounced(campaign_id)
170
+ get "#{@endpoint}/#{campaign_id}/bounced"
171
+ end
172
+
173
+ def unsubscribed(campaign_id)
174
+ get "#{@endpoint}/#{campaign_id}/unsubscribed"
175
+ end
176
+
177
+ def spam(campaign_id)
178
+ get "#{@endpoint}/#{campaign_id}/spam"
179
+ end
180
+
181
+ #
182
+ # Schedules a new campaign to be sent.
183
+ # Parameters:
184
+ # * *list_id* - id of list to send to
185
+ # * *template_id* - id of template to send
186
+ # * *name* - name of a newly created campaign
187
+ # * *from_name* - from name
188
+ # * *reply_to* - reply to
189
+ # * *subject* - subject of campaign
190
+ # * *google_analytics* - true to turn Google Analytics on
191
+ def send(params = {})
192
+ post @endpoint, params
193
+ end
194
+
195
+ #
196
+ # Schedules a new campaign to be sent.
197
+ # Parameters:
198
+ # * *list_id* - id of list to send to
199
+ # * *template_id* - id of template to send
200
+ # * *name* - name of a newly created campaign
201
+ # * *from_name* - from name
202
+ # * *reply_to* - reply to
203
+ # * *subject* - subject of campaign
204
+ # * *google_analytics* - true to turn Google Analytics on
205
+ # * *schedule_for* - Specifies what time a campaign should be sent. If it is provided the campaign will
206
+ # be scheduled to this time, otherwise campaign is sent immediately. The schedule_for
207
+ # must be in ISO date format and should be in the future.
208
+ def schedule(params = {})
209
+ post @endpoint, params
210
+ end
211
+ end
212
+
213
+
214
+ class Contacts
215
+ include ExpressPigeon::API
216
+
217
+ def initialize
218
+ @endpoint = 'contacts'
219
+ end
220
+
221
+ def find_by_email(email)
222
+ get "#{@endpoint}?email=#{email}"
223
+ end
224
+
225
+
226
+ # JSON document represents a contact to be created or updated.
227
+ # The email field is required.
228
+ # When updating a contact, list_id is optional,
229
+ # since the contact is uniquely identified by email across all lists.
230
+ #
231
+ # :param list_id: Contact list ID (Fixnum) the contact will be added to
232
+ #
233
+ # :param contact: Hash describes new contact. The "email" field is required.
234
+ #
235
+ # :returns: representation of a contact
236
+ #
237
+ def upsert(list_id, contact)
238
+ post @endpoint, params = {:list_id => list_id, :contact => contact}
239
+ end
240
+
241
+ # Delete single contact. If list_id is not provided, contact will be deleted from system.
242
+ # :param email: contact email to be deleted.
243
+ # :param list_id: list id to remove contact from, if not provided, contact will be deleted from system.
244
+ def delete(email, list_id = nil)
245
+ if list_id
246
+ query = "email=#{email}&list_id=#{list_id}"
247
+ else
248
+ query = "email=#{email}"
249
+ end
250
+ del "#{@endpoint}?#{query}", nil
251
+ end
252
+
253
+
254
+ end
255
+ class Messages
256
+
257
+ include ExpressPigeon::API
258
+
259
+ def initialize
260
+ @endpoint = 'messages'
261
+ end
262
+
263
+ def send_message(template_id, to, reply_to, from_name, subject, merge_fields = nil, view_online = false, click_tracking = true)
264
+ post @endpoint, params = {template_id: template_id, :to => to, reply_to: reply_to, :from => from_name, :subject => subject,
265
+ :merge_fields => merge_fields, :view_online => view_online, :click_tracking => click_tracking}
266
+ end
267
+
268
+ def report(message_id)
269
+ get "#{@endpoint}/#{message_id}"
270
+ end
271
+
272
+ #
273
+ #
274
+ # start_date is instance of Time
275
+ # end_date is instance of Time
276
+ def reports(from_id, start_date = nil, end_date = nil)
277
+ params = []
278
+
279
+ if from_id
280
+ params << "from_id=#{from_id}"
281
+ end
282
+
283
+ if start_date and not end_date
284
+ raise 'must include both start_date and end_date'
285
+ end
286
+ if end_date and not start_date
287
+ raise 'must include both start_date and end_date'
288
+ end
289
+
290
+ if start_date and end_date
291
+ params << "start_date=#{start_date.strftime('%FT%T.%L%z')}"
292
+ params << "end_date=#{end_date.strftime('%FT%T.%L%z')}"
293
+ end
294
+
295
+ query = "#{@endpoint}?"
296
+
297
+ if params.size > 0
298
+ query << params.join('&')
299
+ end
300
+
301
+ puts "calling: #{query}"
302
+ get query
303
+
304
+ end
305
+
306
+ end
@@ -0,0 +1,109 @@
1
+ require './lib/expresspigeon-ruby'
2
+ require 'pigeon_helper'
3
+
4
+ describe 'campaigns integration test' do
5
+
6
+ include PigeonSpecHelper
7
+ it 'should return more than 0 campaign ids' do
8
+ res = PIGEON.campaigns.all
9
+ res.class.should == Array
10
+ res.size.should > 0
11
+ end
12
+
13
+ it 'cannot send with missing parameters' do
14
+ res = PIGEON.campaigns.send(:template_id => 15233, :name => 'API Test campaign',
15
+ :from_name => 'Igor Polevoy', :reply_to => 'igor@polevoy.org',
16
+ :subject => 'API test', :google_analytics => true)
17
+ validate_response res, 400, 'error', /required parameters: list_id, template_id, name, from_name, reply_to, subject, google_analytics/
18
+ end
19
+
20
+ it 'cannot send with bad reply_to' do
21
+ res = PIGEON.campaigns.send(:list_id => -1, :template_id => -1, :name => 'My Campaign', :from_name => 'John', :reply_to => 'j',
22
+ :subject => 'Hi', :google_analytics => false)
23
+ validate_response res, 400, 'error', /reply_to should be valid email address/
24
+ end
25
+
26
+ it 'cannot send with non-existing template' do
27
+ res = PIGEON.campaigns.send(:list_id => -1, :template_id => -1, :name => 'My Campaign', :from_name => 'John',
28
+ :reply_to => 'j@j.j',
29
+ :subject => 'Hi', :google_analytics => false)
30
+ validate_response res, 400, 'error', /template=-1 is not found/
31
+ end
32
+
33
+ it 'cannot send to non-existing list' do
34
+
35
+ res = PIGEON.campaigns.send(:list_id => -1, :template_id => TEMPLATE_ID, :name => 'My Campaign', :from_name => 'John',
36
+ :reply_to => 'j@j.j',
37
+ :subject => 'Hi', :google_analytics => false)
38
+ validate_response res, 400, 'error', /list=-1 is not found/
39
+ end
40
+
41
+ it 'cannot send to disabled list' do
42
+ res = PIGEON.campaigns.send(:list_id => LIST_ID, :template_id => TEMPLATE_ID, :name => 'My Campaign', :from_name => 'John',
43
+ :reply_to => 'j@j.j',
44
+ :subject => 'Hi', :google_analytics => false)
45
+ validate_response res, 400, 'error', /list=#{DISABLED_LIST} is disabled/
46
+ end
47
+
48
+ it 'should create new list, add contact and send successful campaign' do
49
+
50
+ list_resp = PIGEON.lists.create('My list', 'John', API_USER)
51
+ list_id = list_resp.list.id
52
+ PIGEON.contacts.upsert(list_id, {:email => API_USER})
53
+ resp = PIGEON.campaigns.send(:list_id => list_id, :template_id => TEMPLATE_ID, :name => 'My Campaign', :from_name => 'John',
54
+ :reply_to => API_USER,
55
+ :subject => 'Hi', :google_analytics => false)
56
+ validate_response resp, 200, 'success', /new campaign created successfully/
57
+ report = PIGEON.campaigns.report(resp.campaign_id)
58
+ (report.delivered == 0 or report.delivered == 1).should be_true
59
+ report.clicked.should eq 0
60
+ report.opened.should eq 0
61
+ report.spam.should eq 0
62
+ (report.in_transit == 0 or report.in_transit == 1).should be_true
63
+ report.unsubscribed.should eq 0
64
+ report.bounced.should eq 0
65
+ bounced = PIGEON.campaigns.bounced(resp.campaign_id)
66
+ unsubscribed = PIGEON.campaigns.unsubscribed(resp.campaign_id)
67
+ spam = PIGEON.campaigns.spam(resp.campaign_id)
68
+
69
+ bounced.size.should eq 0
70
+ unsubscribed.size.should eq 0
71
+ spam.size.should eq 0
72
+
73
+ resp = PIGEON.contacts.delete(API_USER)
74
+ validate_response resp, 200, 'success', /contact=non@non.non deleted successfully/
75
+
76
+ resp = PIGEON.contacts.find_by_email(API_USER)
77
+ validate_response resp, 404, 'error', /contact=non@non.non not found/
78
+
79
+ resp = PIGEON.lists.delete(list_id)
80
+ validate_response resp, 200, 'success', /deleted successfully/
81
+ end
82
+
83
+
84
+ it 'cannot send campaign if scheduling with bad date' do
85
+
86
+ list_resp = PIGEON.lists.create "My list", "John", API_USER
87
+ resp = PIGEON.campaigns.schedule :list_id => list_resp.list.id, :template_id => TEMPLATE_ID, :name => 'My Campaign',
88
+
89
+ :from_name => 'John',
90
+ :reply_to => API_USER, :subject => 'Hi',
91
+ :google_analytics => false, :schedule_for => "2013-05-28"
92
+
93
+ validate_response resp, 400, 'error', /schedule_for is not in ISO date format, example: 2013-05-28T17:19:50.779/
94
+ resp = PIGEON.lists.delete(list_resp.list.id)
95
+ validate_response resp, 200, 'success', /deleted successfully/
96
+ end
97
+
98
+ it 'should not schedule campaign with date in the past' do
99
+ list_resp = PIGEON.lists.create('My list', 'John', API_USER)
100
+ resp = PIGEON.campaigns.schedule :list_id => list_resp.list.id, :template_id => TEMPLATE_ID, :name => 'My Campaign',
101
+ :from_name => 'John',
102
+ :reply_to => API_USER, :subject => 'Hi',
103
+ :google_analytics => false, :schedule_for => '2010-05-28T17:19:50.779+0300'
104
+
105
+ validate_response resp, 400, 'error', /schedule_for should be in the future/
106
+ end
107
+
108
+ end
109
+
@@ -0,0 +1,152 @@
1
+ require './lib/expresspigeon-ruby'
2
+ require 'pigeon_helper'
3
+
4
+ describe 'contacts integration test' do
5
+
6
+ include PigeonSpecHelper
7
+
8
+ it 'should not create contact without contact data' do
9
+ resp = PIGEON.contacts.upsert(-1, {})
10
+ validate_response resp, 400, 'error', /contact and contact.email are required/
11
+ end
12
+ it 'should not create contact without email' do
13
+ resp = PIGEON.contacts.upsert -1, :email => '', :first_name => 'Marylin', :last_name => 'Monroe'
14
+ validate_response resp, 400, 'error', /contact and contact.email are required/
15
+ end
16
+
17
+ it 'should not add contact with too many custom fields' do
18
+ custom_fields = {}
19
+ (1..25).each { |n| custom_fields["custom_field_#{n}"] = n }
20
+ resp = PIGEON.contacts.upsert -1, :email => "mary@e.e", :custom_fields => custom_fields
21
+ validate_response resp, 400, 'error', /You cannot create more than 20 custom fields. Use one of the 'custom_fields'./
22
+ end
23
+
24
+ it 'should not create new contact without list_id' do
25
+ resp = PIGEON.contacts.upsert '', :email => 'ee@e.e', :first_name => 'Marylin', :last_name => 'Monroe'
26
+ validate_response resp, 404, 'error', /contact=ee@e.e not found/
27
+ end
28
+
29
+ it 'test_create_with_suppressed_contact' do
30
+ resp = PIGEON.contacts.upsert -1, :email => 'suppressed@e.e'
31
+ validate_response resp, 400, 'error', /contact=suppressed@e.e is in suppress list/
32
+ end
33
+
34
+ it 'cannot create with non-existent_list' do
35
+ resp = PIGEON.contacts.upsert -1, :email => "e@e.e"
36
+ validate_response resp, 404, 'error', /list=-1 not found/
37
+ end
38
+
39
+ it 'creates list with contacts' do
40
+ list_response = PIGEON.lists.create 'My List', 'John Doe', 'john@doe.com'
41
+ list_id = list_response.list.id
42
+ resp = PIGEON.contacts.upsert list_id, email: "mary@e.e",
43
+ :custom_fields => {:custom_field_1 => "custom_value_1", }
44
+ validate_response resp, 200, 'success', /contact=mary@e.e created\/updated successfully/
45
+ resp.contact.custom_fields.custom_field_1.should eq 'custom_value_1'
46
+ resp.contact.email.should eq 'mary@e.e'
47
+ resp.contact.email_format.should eq 'html'
48
+ resp.contact.status.should eq 'ACTIVE'
49
+ PIGEON.lists.delete(list_id)
50
+ end
51
+
52
+ it 'creates list non-existent custom field' do
53
+ list_response = PIGEON.lists.create 'My List', 'John Doe', 'a@a.a'
54
+ list_id = list_response.list.id
55
+ resp = PIGEON.contacts.upsert(list_id, {:email => "mary@e.e", :custom_fields => {:c => "c", }})
56
+ validate_response resp, 200, 'success', nil
57
+ PIGEON.lists.delete(list_id)
58
+ end
59
+
60
+ it 'cannot export contacts from list without list_id' do
61
+ content = ''
62
+ PIGEON.lists.csv "-1" do |c|
63
+ content << c
64
+ end
65
+ resp = JSON.parse(content)
66
+ validate_response MetaHash.new(resp), 404, 'error', /list=-1 not found/
67
+ end
68
+
69
+ it 'should get contacts from suppressed list' do
70
+ content =''
71
+ PIGEON.lists.csv "suppress_list" do |c|
72
+ content += c
73
+ end
74
+ resp = content.split /\n/
75
+ resp.size.should eq 2
76
+ resp[1].should =~ /"suppressed@e.e","Suppressed","Doe"/
77
+ end
78
+
79
+ it 'should get single contact' do
80
+ resp = PIGEON.contacts.find_by_email 'suppressed@e.e'
81
+ resp.email.should eq 'suppressed@e.e'
82
+ end
83
+
84
+ it 'should not find non existent contact' do
85
+ resp = PIGEON.contacts.find_by_email 'a@a.a'
86
+ validate_response resp, 404, 'error', /contact=a@a.a not found/
87
+ end
88
+
89
+ it 'should update contact' do
90
+
91
+ list_response = PIGEON.lists.create('My List', 'John Doe', "a@a.a")
92
+ resp = PIGEON.contacts.upsert list_response.list.id,
93
+ :email => "mary@e.e", :first_name => "Mary", :last_name => "Doe"
94
+ validate_response resp, 200, 'success', /contact=mary@e.e created\/updated successfully/
95
+ PIGEON.contacts.find_by_email("mary@e.e").last_name.should eq 'Doe'
96
+
97
+ resp = PIGEON.contacts.upsert list_response.list.id,
98
+ :email => 'mary@e.e', :first_name => 'Mary', :last_name => 'Johns'
99
+ validate_response resp, 200, 'success', /contact=mary@e.e created\/updated successfully/
100
+ PIGEON.contacts.find_by_email("mary@e.e").last_name.should eq 'Johns'
101
+ end
102
+
103
+ it 'cannot delete contact with non-existent email' do
104
+ res = PIGEON.contacts.delete("g@g.g")
105
+ validate_response res, 404, 'error', /contact=g@g.g not found/
106
+ end
107
+
108
+ it 'should not delete suppressed contact' do
109
+ res = PIGEON.contacts.delete("suppressed@e.e")
110
+ validate_response res, 400, 'error', /contact=suppressed@e.e is in suppress list/
111
+ end
112
+
113
+ it 'should delete single contact from all lists' do
114
+ list_response = PIGEON.lists.create 'My List', 'Jane Doe', 'a@a.a'
115
+ PIGEON.contacts.upsert list_response.list.id, :email => 'mary@e.e'
116
+ res = PIGEON.contacts.delete 'mary@e.e'
117
+ validate_response res, 200, 'success', /contact=mary@e.e deleted successfully/
118
+ PIGEON.lists.delete list_response.list.id
119
+ end
120
+
121
+ it 'deletes single contact from single list' do
122
+ list_response = PIGEON.lists.create 'My List', 'John D.', 'a@a.a'
123
+ list_response_2 = PIGEON.lists.create('My List2', "Jane D.", 'a@a.a')
124
+ PIGEON.contacts.upsert(list_response.list.id, {:email => 'mary@e.e'})
125
+ PIGEON.contacts.upsert(list_response_2.list.id, {:email => 'mary@e.e'})
126
+
127
+ res = PIGEON.contacts.delete 'mary@e.e', list_response.list.id
128
+
129
+ validate_response res, 200, 'success', /contact=mary@e.e deleted successfully/
130
+
131
+ contacts_exported = ''
132
+ PIGEON.lists.csv list_response.list.id do |c|
133
+ contacts_exported << c
134
+ end
135
+ contacts_exported = contacts_exported.split /\n/
136
+ contacts_exported.size.should eq 1
137
+
138
+ contacts_exported_2 = ''
139
+ PIGEON.lists.csv list_response_2.list.id do |c|
140
+ contacts_exported_2 << c
141
+ end
142
+
143
+ contacts_exported_2 = contacts_exported_2.split /\n/
144
+ contacts_exported_2.size.should eq 2
145
+ contacts_exported_2[1].should =~ /"mary@e.e"/
146
+
147
+ PIGEON.lists.delete(list_response.list.id)
148
+ PIGEON.lists.delete(list_response_2.list.id)
149
+ PIGEON.contacts.delete('mary@e.e')
150
+ end
151
+ end
152
+
@@ -0,0 +1,149 @@
1
+ require './lib/expresspigeon-ruby'
2
+ require 'pigeon_helper'
3
+
4
+ describe 'contacts integration test' do
5
+
6
+ include PigeonSpecHelper
7
+
8
+ it 'test_create_and_delete_new_list(self):' do
9
+ contact_list = PIGEON.lists.create 'Active customers', 'Bob', 'bob@acmetools.com'
10
+
11
+ puts "*****************************"
12
+ puts contact_list
13
+
14
+ validate_response contact_list, 200, 'success', /list=#{contact_list.list.id} created\/updated successfully/
15
+ contact_list.list.name.should eq "Active customers"
16
+ contact_list.list.from_name.should eq "Bob"
17
+ contact_list.list.reply_to.should eq "bob@acmetools.com"
18
+ contact_list.list.contact_count.should eq 0
19
+
20
+ #TODO: uncomment when running against real test env
21
+ #contact_list.list.zip.should eq '220000'
22
+ #contact_list.list.state.should eq "AL"
23
+ #contact_list.list.address1.should eq "Coolman 11"
24
+ #contact_list.list.city.should eq "Minsk"
25
+ #contact_list.list.country.should eq "Belarus"
26
+ #contact_list.list.organization.should eq "ExpressPigeon"
27
+
28
+ res = PIGEON.lists.delete(contact_list.list.id)
29
+ validate_response res, 200, 'success', /list=#{contact_list.list.id} deleted successfully/
30
+ end
31
+
32
+ #TODO: implement Lists.update method
33
+ it 'should update existing list' do
34
+
35
+ existing_list = PIGEON.lists.create("Update", "Bob", "bob@acmetools.com")
36
+ #res = PIGEON.lists.update existing_list.list.id, :name => 'Updated Name', :from_name => 'Bill'
37
+ #
38
+ #validate_response res, 200, 'success', /list=#{res.list.id} created\/updated successfully/
39
+ #res.list.name.should eq "Updated Name"
40
+ #res.list.from_name.should eq 'Bill'
41
+ #PIGEON.lists.delete res.list.id
42
+ end
43
+
44
+
45
+ it 'should upload contacts'
46
+
47
+ list_name = "Upload_#{Kernel.rand(9999).to_s}"
48
+ existing_list = PIGEON.lists.create(list_name, 'Bob', 'bob@acmetools.com')
49
+
50
+ #res = PIGEON.lists.upload(existing_list.list.id, self.file_to_upload)
51
+
52
+
53
+ #self.assertEqual(res.status, "success")
54
+ #self.assertEqual(res.code, 200)
55
+ #self.assertEquals(res.message, "file uploaded successfully")
56
+ #self.assertTrue(res.upload_id is not None)
57
+ #
58
+ #sleep(5)
59
+ #
60
+ #res = self.api.lists.upload_status(res.upload_id)
61
+ #self.assertEqual(res.message, "file upload completed")
62
+ #self.assertEqual(res.status, "success")
63
+ #self.assertEqual(res.code, 200)
64
+ #report = res.report
65
+ #self.assertTrue(report.completed)
66
+ #self.assertFalse(report.failed)
67
+ #self.assertEqual(report.suppressed, 0)
68
+ #self.assertEqual(report.skipped, 0)
69
+ #self.assertEqual(report.list_name, list_name)
70
+ #self.assertEqual(report.imported, 2)
71
+
72
+ # def test_upsert_list_with_non_existent_id(self):
73
+ # res = self.api.lists.update(-1, {"name": "Updated Name", "from_name": "Bill"})
74
+ # self.assertEqual(res.status, "error")
75
+ # self.assertEqual(res.code, 404)
76
+ # self.assertEqual(res.message, "list=-1 not found")
77
+ #
78
+ # def test_delete_list_with_non_existent_id(self):
79
+ # res = self.api.lists.delete(-1)
80
+ # self.assertEqual(res.status, "error")
81
+ # self.assertEqual(res.code, 404)
82
+ # self.assertEqual(res.message, "list=-1 not found")
83
+ #
84
+ # def test_remove_disabled_list(self):
85
+ # res = self.api.lists.delete(130)
86
+ # self.assertEqual(res.code, 400)
87
+ # self.assertEqual(res.status, "error")
88
+ # self.assertEqual(res.message, "could not delete disabled list=130")
89
+ #
90
+ # def test_upload_without_id(self):
91
+ # res = self.api.lists.upload("", self.file_to_upload)
92
+ # self.assertEqual(res.code, 400)
93
+ # self.assertEqual(res.status, "error")
94
+ # self.assertEqual(res.message, "you must provide list_id in URL")
95
+ #
96
+ # def test_upload_with_non_existent_id(self):
97
+ # res = self.api.lists.upload(-1, self.file_to_upload)
98
+ # self.assertEqual(res.code, 404)
99
+ # self.assertEqual(res.message, "list=-1 not found")
100
+ #
101
+ # def test_upload_status_without_upload_id(self):
102
+ # res = self.api.lists.upload_status("")
103
+ # self.assertEqual(res.code, 400)
104
+ # self.assertEqual(res.status, "error")
105
+ # self.assertEqual(res.message, "you must provide upload id")
106
+ #
107
+ # def test_enabled_list_removal(self):
108
+ # list_resp = self.api.lists.create("My list", "John", os.environ['EXPRESSPIGEON_API_USER'])
109
+ # self.api.contacts.upsert(list_resp.list.id, {"email": os.environ['EXPRESSPIGEON_API_USER']})
110
+ #
111
+ # now = datetime.datetime.now(pytz.UTC)
112
+ # schedule = self.format_date(now + datetime.timedelta(hours=1))
113
+ #
114
+ # res = self.api.campaigns.schedule(list_id=list_resp.list.id, template_id=self.template_id, name="My Campaign",
115
+ # from_name="John",
116
+ # reply_to=os.environ['EXPRESSPIGEON_API_USER'], subject="Hi",
117
+ # google_analytics=False,
118
+ # schedule_for=schedule)
119
+ # self.assertEqual(res.code, 200)
120
+ # self.assertEqual(res.status, "success")
121
+ # self.assertEqual(res.message, "new campaign created successfully")
122
+ # self.assertTrue(res.campaign_id is not None)
123
+ #
124
+ # res = self.api.lists.delete(list_resp.list.id)
125
+ # self.assertEqual(res.code, 400)
126
+ # self.assertEqual(res.status, "error")
127
+ # self.assertEqual(res.message,
128
+ # "could not delete list={0}, it has dependent subscriptions and/or scheduled campaigns".format(
129
+ # list_resp.list.id))
130
+ #
131
+ # def test_export_csv(self):
132
+ # list_response = self.api.lists.create("My List", "a@a.a", "a@a.a")
133
+ # self.api.contacts.upsert(list_response.list.id, {"email": "mary@a.a"})
134
+ #
135
+ # res = self.api.lists.csv(list_response.list.id).split("\n")
136
+ # self.assertEquals(len(res), 2)
137
+ # headers = '"Email", "First name", "Last name", "City", "Phone", "Company", "Title", "Address 1", "Address 2", ' \
138
+ # '"State", "Zip", "Country", "Date of birth", "custom_field_1", "custom_field_10", "custom_field_11", ' \
139
+ # '"custom_field_12", "custom_field_13", "custom_field_18", "custom_field_19", "custom_field_2", ' \
140
+ # '"custom_field_20", "custom_field_21", "custom_field_22", "custom_field_23", "custom_field_24", ' \
141
+ # '"custom_field_3", "custom_field_4", "custom_field_5", "custom_field_6", "custom_field_7", ' \
142
+ # '"custom_field_8", "custom_field_9"'
143
+ # self.assertEquals(res[0], headers)
144
+ # self.assertEquals(res[1], '"mary@a.a",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,')
145
+ #
146
+ # self.api.lists.delete(list_response.list.id)
147
+ # self.api.contacts.delete("mary@a.a")
148
+
149
+ end
@@ -0,0 +1,127 @@
1
+ require './lib/expresspigeon-ruby'
2
+ require 'pigeon_helper'
3
+
4
+ describe 'transactional messages integration test' do
5
+
6
+ include PigeonSpecHelper
7
+
8
+ #def test_sending_message_and_report_without_params(self):
9
+ # res = self.api.messages.send_message(template_id=-1, to="", reply_to="", from_name="", subject="")
10
+ # self.assertEqual(res.code, 400)
11
+ # self.assertEqual(res.status, "error")
12
+ # self.assertEqual(res.message, "Required fields: template_id, reply_to, from, to, and subject")
13
+ #
14
+ #def test_sending_message_and_report_with_wrong_email_in_to(self):
15
+ # res = self.api.messages.send_message(template_id=-1, to="e", reply_to="a@a.a", from_name="me", subject="Hi")
16
+ # self.assertEqual(res.code, 400)
17
+ # self.assertEqual(res.status, "error")
18
+ # self.assertEqual(res.message, "Email in the 'to' field is not valid")
19
+ #
20
+ #def test_sending_message_and_report_with_wrong_email_in_reply_to(self):
21
+ # res = self.api.messages.send_message(template_id=-1, to="e@e.e", reply_to="a", from_name="me", subject="Hi")
22
+ # self.assertEqual(res.code, 400)
23
+ # self.assertEqual(res.status, "error")
24
+ # self.assertEqual(res.message, "Email in the 'reply_to' field is not valid")
25
+ #
26
+ #def test_sending_message_and_report_with_wrong_template_id(self):
27
+ # res = self.api.messages.send_message(template_id=-1, to="e@e.e", reply_to="a@a.a", from_name="me", subject="Hi")
28
+ # self.assertEqual(res.code, 400)
29
+ # self.assertEqual(res.status, "error")
30
+ # self.assertEqual(res.message, "template=-1 not found")
31
+
32
+
33
+ #TODO: complete the spec
34
+ it 'sends a single transactional message' do
35
+ message_response = PIGEON.messages.send_message 4905, ENV['TARGET_EMAIL'], ENV['TARGET_EMAIL'], "Team ExpressPigeon", "Hi there!",
36
+ :first_name => "Igor"
37
+ validate_response message_response, 200, 'success', /email queued/
38
+ report = PIGEON.messages.report(message_response.id)
39
+ report.id.should eq message_response.id
40
+ end
41
+
42
+
43
+ #
44
+ #def test_reports_with_bad_dates(self):
45
+ # res = self.api.messages.reports("abc", "")
46
+ # self.assertEquals(res.code, 400)
47
+ # self.assertEquals(res.status, "error")
48
+ # self.assertEquals(res.message, "invalid 'start_date' or 'end_date'")
49
+ #
50
+ #def test_reports_with_start_date_only(self):
51
+ # res = self.api.messages.reports("2013-03-16T11:22:23.210+0000", "")
52
+ # self.assertEquals(res.code, 400)
53
+ # self.assertEquals(res.status, "error")
54
+ # self.assertEquals(res.message, "'start_date' and 'end_date' should be provided together")
55
+ #
56
+ #def test_reports_with_end_date_only(self):
57
+ # res = self.api.messages.reports("", "2013-03-16T11:22:23.210+0000")
58
+ # self.assertEquals(res.code, 400)
59
+ # self.assertEquals(res.status, "error")
60
+ # self.assertEquals(res.message, "'start_date' and 'end_date' should be provided together")
61
+ #
62
+ #def test_sending_multiple_messages_and_get_reports(self):
63
+ # message_response = self.api.messages.send_message(template_id=self.template_id,
64
+ # to=os.environ['EXPRESSPIGEON_API_USER'],
65
+ # reply_to="a@a.a", from_name="me", subject="Hi",
66
+ # merge_fields={"first_name": "Gleb"})
67
+ # self.assertEqual(message_response.code, 200)
68
+ # self.assertEqual(message_response.status, "success")
69
+ # self.assertEqual(message_response.message, "email queued")
70
+ # self.assertTrue(message_response.id is not None and message_response.id != "")
71
+ #
72
+ # message_response_2 = self.api.messages.send_message(template_id=self.template_id,
73
+ # to=os.environ['EXPRESSPIGEON_API_USER'],
74
+ # reply_to="a@a.a", from_name="me", subject="Hi 2",
75
+ # merge_fields={"first_name": "Gleb"})
76
+ # self.assertEqual(message_response_2.code, 200)
77
+ # self.assertEqual(message_response_2.status, "success")
78
+ # self.assertEqual(message_response_2.message, "email queued")
79
+ # self.assertTrue(message_response_2.id is not None and message_response.id != "")
80
+ #
81
+ # report = self.__get_report_by_id__(message_response.id)
82
+ #
83
+ # self.assertEquals(report.id, message_response.id)
84
+ # self.assertEquals(report.email, os.environ['EXPRESSPIGEON_API_USER'])
85
+ # self.assertTrue(report.in_transit is not None)
86
+ #
87
+ # report2 = self.__get_report_by_id__(message_response_2.id)
88
+ #
89
+ # self.assertEquals(report2.id, message_response_2.id)
90
+ # self.assertEquals(report2.email, os.environ['EXPRESSPIGEON_API_USER'])
91
+ # self.assertTrue(report2.in_transit is not None)
92
+ #
93
+ it 'test_sending_multiple_messages_and_get_reports_for_today(self):' do
94
+
95
+ start = Time.now.utc - 60 # one minute ago
96
+
97
+ message_response = PIGEON.messages.send_message 4905, ENV['TARGET_EMAIL'], ENV['TARGET_EMAIL'],
98
+ 'Team EP', "Hi, there!", :first_name => "Bob"
99
+
100
+ validate_response message_response, 200, 'success', /email queued/
101
+ message_response.id should_not be_nil
102
+
103
+ message_response2 = PIGEON.messages.send_message 4905, ENV['TARGET_EMAIL'], ENV['TARGET_EMAIL'],
104
+ 'Team EP', "Hi, there!", :first_name => "Bob"
105
+ validate_response message_response2, 200, 'success', /email queued/
106
+ message_response2.id should_not be_nil
107
+
108
+ finish = start + 120 # two minutes after start
109
+ reports = PIGEON.messages.reports (message_response.id - 1), start, finish
110
+
111
+ reports.size.should eq 2
112
+ reports[0]['id'].should eq message_response.id
113
+ reports[1]['id'].should eq message_response2.id
114
+
115
+ reports[0]['email'].should eq ENV['TARGET_EMAIL']
116
+ reports[1]['email'].should eq ENV['TARGET_EMAIL']
117
+ end
118
+ #
119
+ #def __get_report_by_id__(self, message_id, start_date=None, end_date=None):
120
+ # reports = self.api.messages.reports() if start_date is None and end_date is None else \
121
+ # self.api.messages.reports(start_date, end_date)
122
+ # report = [r for r in reports if r.id == message_id]
123
+ # self.assertEquals(len(report), 1)
124
+ # return report[0]
125
+
126
+
127
+ end
@@ -0,0 +1,16 @@
1
+ PIGEON ||= ExpressPigeon::API
2
+ TEMPLATE_ID ||= 1
3
+ LIST_ID ||= 12
4
+ API_USER ||= "non@non.non"
5
+ DISABLED_LIST ||= 12
6
+
7
+ module PigeonSpecHelper
8
+ def validate_response res, code, status, message
9
+ res.code.should eq code
10
+ res.status.should eq status
11
+ if message
12
+ (res.message =~ message).should_not be_nil
13
+ end
14
+
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: expresspigeon-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - ipolevoy
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-01-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.6'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.6'
30
+ description: ExpressPigeon Ruby API for sending transactional messages, manipulating
31
+ lists, contacts nad more
32
+ email:
33
+ - ipolevoy@groupon.com
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - .gitignore
39
+ - .rvmrc
40
+ - Gemfile
41
+ - Gemfile.lock
42
+ - LICENSE.txt
43
+ - README.md
44
+ - Rakefile
45
+ - expresspigeon-ruby.gemspec
46
+ - lib/expresspigeon-ruby.rb
47
+ - lib/expresspigeon-ruby/version.rb
48
+ - spec/campaigns_spec.rb
49
+ - spec/contacts_spec.rb
50
+ - spec/lists_spec.rb
51
+ - spec/messages_spec.rb
52
+ - spec/pigeon_helper.rb
53
+ homepage: https://github.com/expresspigeon/expresspigeon-ruby
54
+ licenses: []
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 1.8.25
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: ExpressPigeon API Ruby Wrapper
77
+ test_files:
78
+ - spec/campaigns_spec.rb
79
+ - spec/contacts_spec.rb
80
+ - spec/lists_spec.rb
81
+ - spec/messages_spec.rb
82
+ - spec/pigeon_helper.rb