express_pigeon 2.0.2 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.hound.yml +3 -0
- data/CHANGELOG.md +15 -0
- data/CONTRIBUTING.md +9 -0
- data/Gemfile +1 -0
- data/Guardfile +2 -36
- data/README.md +57 -0
- data/lib/express_pigeon/auto_responders.rb +70 -0
- data/lib/express_pigeon/campaigns.rb +120 -0
- data/lib/express_pigeon/contacts.rb +15 -4
- data/lib/express_pigeon/messages.rb +83 -0
- data/lib/express_pigeon/templates.rb +24 -0
- data/lib/express_pigeon/version.rb +1 -1
- data/lib/express_pigeon.rb +4 -11
- data/spec/lib/express_pigeon/auto_responders_spec.rb +2 -0
- data/spec/lib/express_pigeon/campaigns_spec.rb +2 -0
- data/spec/{express_pigeon → lib/express_pigeon}/contacts_spec.rb +0 -0
- data/spec/{express_pigeon → lib/express_pigeon}/lists_spec.rb +0 -0
- data/spec/lib/express_pigeon/messages_spec.rb +37 -0
- data/spec/lib/express_pigeon/templates_spec.rb +43 -0
- data/spec/spec_helper.rb +3 -18
- data.tar.gz.sig +2 -2
- metadata +19 -22
- metadata.gz.sig +0 -0
- data/lib/express_pigeon/api/campaigns.rb +0 -62
- data/lib/express_pigeon/api/contacts.rb +0 -42
- data/lib/express_pigeon/api/lists.rb +0 -49
- data/lib/express_pigeon/api/messages.rb +0 -56
- data/lib/express_pigeon/api.rb +0 -66
- data/lib/express_pigeon/autoresponders.rb +0 -0
- data/lib/express_pigeon/meta_hash.rb +0 -20
- data/lib/express_pigeon/transactional_emails.rb +0 -4
- data/spec/express_pigeon/api/campaigns_spec.rb +0 -100
- data/spec/express_pigeon/api/contacts_spec.rb +0 -146
- data/spec/express_pigeon/api/lists_spec.rb +0 -23
- data/spec/express_pigeon/api/messages_spec.rb +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dec6639b9221476fba9c7129782bcafcd0f8865d
|
4
|
+
data.tar.gz: 013b35a5e0fd1c7d6649a7bfb8e9419c148cb849
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0179a1e751bb4a2724e2ba5235bf55fb40df81e11c9783c084770748f8014ec3d9f8844ebb2eb0857e4434eee2d38ea19637f577200cca0ea3e45fb01c9740bb
|
7
|
+
data.tar.gz: 2f67667b090857f6a0c793271879262d9a438a5258ea7a7c35f6603a285165b44f96ad5c492773affa5cb701e54067c0e0ee17211aef6ab588e5f2f8b7d9b8e8
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/.hound.yml
ADDED
data/CHANGELOG.md
ADDED
data/CONTRIBUTING.md
CHANGED
@@ -3,6 +3,15 @@
|
|
3
3
|
**DO _NOT_ RUN THE SPECS AGAINST YOUR PRODUCTION ACCOUNT. THEY ARE INTEGRATION TESTS AND WILL CREATE LISTS, CONTACTS, ETC INSIDE YOUR ACCOUNT.**
|
4
4
|
|
5
5
|
- Create a "dummy" account on [https://expresspigeon.com](https://expresspigeon.com).
|
6
|
+
|
7
|
+
[](https://expresspigeon.com/newsletters/)
|
8
|
+
|
9
|
+
[](https://expresspigeon.com/newsletters/)
|
10
|
+
|
11
|
+
[](https://expresspigeon.com/newsletters/)
|
12
|
+
|
13
|
+
- Set the `TEST_TEMPLATE_ID` to the example newsletter template id.
|
14
|
+
|
6
15
|
- Create three custom_fields on the account.
|
7
16
|
- `my_custom_text_field` as `text` (no default)
|
8
17
|
- `my_custom_number_field` as `number` (no default)
|
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -1,49 +1,15 @@
|
|
1
|
-
|
2
|
-
# More info at https://github.com/guard/guard#readme
|
3
|
-
|
4
|
-
## Uncomment and set this to only include directories you want to watch
|
5
|
-
directories %w(. lib spec)
|
6
|
-
|
7
|
-
## Uncomment to clear the screen before every task
|
1
|
+
directories %w(. lib lib/express_pigeon spec spec/lib spec/lib/express_pigeon)
|
8
2
|
clearing :on
|
9
3
|
|
10
|
-
## Make Guard exit when config is changed so it can be restarted
|
11
|
-
#
|
12
|
-
## Note: if you want Guard to automatically start up again, run guard in a
|
13
|
-
## shell loop, e.g.:
|
14
|
-
#
|
15
|
-
# $ while bundle exec guard; do echo "Restarting Guard..."; done
|
16
|
-
#
|
17
|
-
## Note: if you are using the `directories` clause above and you are not
|
18
|
-
## watching the project directory ('.'), the you will want to move the Guardfile
|
19
|
-
## to a watched dir and symlink it back, e.g.
|
20
|
-
#
|
21
|
-
# $ mkdir config
|
22
|
-
# $ mv Guardfile config/
|
23
|
-
# $ ln -s config/Guardfile .
|
24
|
-
#
|
25
|
-
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
26
|
-
#
|
27
4
|
watch ('Guardfile') do
|
28
5
|
UI.info 'Exiting because Guard must be restarted for changes to take effect'
|
29
6
|
exit 0
|
30
7
|
end
|
31
8
|
|
32
|
-
|
33
|
-
# rspec may be run, below are examples of the most common uses.
|
34
|
-
# * bundler: 'bundle exec rspec'
|
35
|
-
# * bundler binstubs: 'bin/rspec'
|
36
|
-
# * spring: 'bin/rspec' (This will use spring if running and you have
|
37
|
-
# installed the spring binstubs per the docs)
|
38
|
-
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
39
|
-
# * 'just' rspec: 'rspec'
|
40
|
-
|
41
|
-
guard :rspec, cmd: 'bundle exec rspec' do
|
9
|
+
guard :rspec, cmd: 'bundle exec rspec --warnings' do
|
42
10
|
require 'guard/rspec/dsl'
|
43
11
|
dsl = Guard::RSpec::Dsl.new(self)
|
44
12
|
|
45
|
-
# Feel free to open issues for suggestions and improvements
|
46
|
-
|
47
13
|
# RSpec files
|
48
14
|
rspec = dsl.rspec
|
49
15
|
watch(rspec.spec_helper) { rspec.spec_dir }
|
data/README.md
CHANGED
@@ -1,5 +1,62 @@
|
|
1
1
|
[](https://semaphoreapp.com/just3ws/express_pigeon)
|
2
2
|
[](https://codeclimate.com/github/just3ws/express_pigeon)
|
3
|
+
[](http://badge.fury.io/rb/express_pigeon)
|
4
|
+
[](http://inch-ci.org/github/just3ws/express_pigeon)
|
3
5
|
[](https://codeclimate.com/github/just3ws/express_pigeon)
|
4
6
|
|
5
7
|
Another Ruby client for ExpressPigeon.
|
8
|
+
|
9
|
+
[](https://github.com/just3ws/express_pigeon/blob/master/CONTRIBUTING.md)
|
10
|
+
|
11
|
+
# ExpressPigeon API Endpoints
|
12
|
+
|
13
|
+
## Implemented
|
14
|
+
|
15
|
+
### ExpressPigeon::Contacts
|
16
|
+
|
17
|
+
- `#create` **POST** `https://api.expresspigeon.com/contacts`
|
18
|
+
- `#delete` **DELETE** `https://api.expresspigeon.com/contacts`
|
19
|
+
- `#find` **GET** `https://api.expresspigeon.com/contacts`
|
20
|
+
- `#move` **POST** `https://api.expresspigeon.com/contacts/move`
|
21
|
+
|
22
|
+
### ExpressPigeon::Lists
|
23
|
+
|
24
|
+
- `#create` **POST** `https://api.expresspigeon.com/lists`
|
25
|
+
- `#delete` **DELETE** `https://api.expresspigeon.com/lists/{id}`
|
26
|
+
- `#download_csv` **GET** `https://api.expresspigeon.com/lists/{list_id}/csv`
|
27
|
+
- `#index` **GET** `https://api.expresspigeon.com/lists`
|
28
|
+
- `#update` **PUT** `https://api.expresspigeon.com/lists`
|
29
|
+
- `#upload_status` **GET** `https://api.expresspigeon.com/lists/upload_status/{id}`
|
30
|
+
- `#upload` **POST** `https://api.expresspigeon.com/lists/{id}/upload`
|
31
|
+
|
32
|
+
### ExpressPigeon::Messages
|
33
|
+
|
34
|
+
- `#send` **POST** `https://api.expresspigeon.com/messages`
|
35
|
+
- `#status` **GET** `https://api.expresspigeon.com/messages/{id}`
|
36
|
+
- `#statuses` **GET** `https://api.expresspigeon.com/messages`
|
37
|
+
|
38
|
+
### ExpressPigeon::Templates
|
39
|
+
|
40
|
+
- `#copy` **POST** `https://api.expresspigeon.com/templates/{template_id}/copy`
|
41
|
+
|
42
|
+
### ExpressPigeon::Campaigns
|
43
|
+
|
44
|
+
- `#create` **POST** `https://api.expresspigeon.com/campaigns`
|
45
|
+
- `#index` **GET** `https://api.expresspigeon.com/campaigns`
|
46
|
+
- `#report_bounced` **GET** `https://api.expresspigeon.com/campaigns/{campaign_id}/bounced`
|
47
|
+
- `#report_clicked` **GET** `https://api.expresspigeon.com/campaigns/{campaign_id}/clicked`
|
48
|
+
- `#report_opened` **GET** `https://api.expresspigeon.com/campaigns/{campaign_id}/opened`
|
49
|
+
- `#report_spam` **GET** `https://api.expresspigeon.com/campaigns/{campaign_id}/spam`
|
50
|
+
- `#report_unsubscribed` **GET** `https://api.expresspigeon.com/campaigns/{campaign_id}/unsubscribed`
|
51
|
+
- `#report` **GET** `https://api.expresspigeon.com/campaigns/{campaign_id}`
|
52
|
+
|
53
|
+
### ExpressPigeon::AutoResponders
|
54
|
+
|
55
|
+
- `#index` **GET** `https://api.expresspigeon.com/auto_responders`
|
56
|
+
- `#report_bounced` **GET** `https://api.expresspigeon.com/auto_responders/{auto_responder_id}/{auto_responder_part_id}/bounced`
|
57
|
+
- `#report_spam` **GET** `https://api.expresspigeon.com/auto_responders/{auto_responder_id}/{auto_responder_part_id}/unsubscribed`
|
58
|
+
- `#report_unsubscribed` **GET** `https://api.expresspigeon.com/auto_responders/{auto_responder_id}/{auto_responder_part_id}/spam`
|
59
|
+
- `#report` **GET** `https://api.expresspigeon.com/auto_responders/{auto_responder_id}`
|
60
|
+
- `#start` **POST** `https://api.expresspigeon.com/auto_responders/{auto_responder_id}/start`
|
61
|
+
- `#stop` **POST** `https://api.expresspigeon.com/auto_responders/{auto_responder_id}/stop`
|
62
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module ExpressPigeon
|
2
|
+
# AutoResponders
|
3
|
+
class AutoResponders
|
4
|
+
include HTTParty
|
5
|
+
base_uri('https://api.expresspigeon.com/auto_responders')
|
6
|
+
|
7
|
+
# Get all autoresponders
|
8
|
+
#
|
9
|
+
# GET https://api.expresspigeon.com/auto_responders
|
10
|
+
def index
|
11
|
+
self.class.get('')
|
12
|
+
end
|
13
|
+
|
14
|
+
# Start for a contact
|
15
|
+
#
|
16
|
+
# POST https://api.expresspigeon.com/auto_responders/{auto_responder_id}/start
|
17
|
+
def start(auto_responder_id, email_address)
|
18
|
+
options = {}
|
19
|
+
options['email'] = email_address
|
20
|
+
|
21
|
+
self.class.post(
|
22
|
+
"/#{auto_responder_id}/start",
|
23
|
+
body: options.to_json,
|
24
|
+
headers: { 'Content-Type' => 'application/json' }
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Stop for a contact
|
29
|
+
#
|
30
|
+
# POST https://api.expresspigeon.com/auto_responders/{auto_responder_id}/stop
|
31
|
+
def stop(auto_responder_id, email_address)
|
32
|
+
options = {}
|
33
|
+
options['email'] = email_address
|
34
|
+
|
35
|
+
self.class.post(
|
36
|
+
"/#{auto_responder_id}/stop",
|
37
|
+
body: options.to_json,
|
38
|
+
headers: { 'Content-Type' => 'application/json' }
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Report for a single autoresponder
|
43
|
+
#
|
44
|
+
# GET https://api.expresspigeon.com/auto_responders/{auto_responder_id}
|
45
|
+
def report(auto_responder_id)
|
46
|
+
self.class.get("/#{auto_responder_id}")
|
47
|
+
end
|
48
|
+
|
49
|
+
# Get bounced contacts for autoresponder part
|
50
|
+
#
|
51
|
+
# GET https://api.expresspigeon.com/auto_responders/{auto_responder_id} /{auto_responder_part_id}/bounced
|
52
|
+
def report_bounced(auto_responder_id, auto_responder_part_id)
|
53
|
+
self.class.get("/#{auto_responder_id}/#{auto_responder_part_id}/bounced")
|
54
|
+
end
|
55
|
+
|
56
|
+
# Get unsubscribed contacts for autoresponder part
|
57
|
+
#
|
58
|
+
# GET https://api.expresspigeon.com/auto_responders/{auto_responder_id} /{auto_responder_part_id}/unsubscribed
|
59
|
+
def report_unsubscribed(auto_responder_id, auto_responder_part_id)
|
60
|
+
self.class.get("/#{auto_responder_id}/#{auto_responder_part_id}/unsubscribed")
|
61
|
+
end
|
62
|
+
|
63
|
+
# Get spam contacts for autoresponder part
|
64
|
+
#
|
65
|
+
# GET https://api.expresspigeon.com/auto_responders/{auto_responder_id} /{auto_responder_part_id}/spam
|
66
|
+
def report_spam(auto_responder_id, auto_responder_part_id)
|
67
|
+
self.class.get("/#{auto_responder_id}/#{auto_responder_part_id}/spam")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module ExpressPigeon
|
2
|
+
# Campaigns
|
3
|
+
#
|
4
|
+
# Campaign API provides the same service as sending email campaigns from the
|
5
|
+
# website. A campaign consists of newsletter template, subject, from name,
|
6
|
+
# reply to, and a lists of contacts a campaign can be sent to.
|
7
|
+
class Campaigns
|
8
|
+
include HTTParty
|
9
|
+
base_uri('https://api.expresspigeon.com/campaigns')
|
10
|
+
debug_output($stderr)
|
11
|
+
|
12
|
+
# Campaigns creation
|
13
|
+
#
|
14
|
+
# POST https://api.expresspigeon.com/campaigns
|
15
|
+
#
|
16
|
+
# list_id: The id of a list the campaign is sent to. The list must
|
17
|
+
# be enabled.
|
18
|
+
# template_id: The id of a newsletter template used for the campaign.
|
19
|
+
# name: The name of a campaign. This name is for your reference
|
20
|
+
# only and will not be exposed to your audience. If you
|
21
|
+
# have Google Analytics turned on, this value will also be
|
22
|
+
# used for Google Analytics campaign.
|
23
|
+
# from_name: This parameter is displayed as "From" field in the email
|
24
|
+
# program when your recipients view your message. Use this
|
25
|
+
# value to clearly state your name or name of your organization.
|
26
|
+
# reply_to: This parameter specifies the email address which will
|
27
|
+
# be getting replies from your recipients should they
|
28
|
+
# choose to reply. The reply_to should be a valid email address.
|
29
|
+
# subject: The subject of a newsletter
|
30
|
+
# google_analytics: Indicates whether Google Analytics should be enabled
|
31
|
+
# for a campaign. Should be true or false.
|
32
|
+
# schedule_for: Specifies what time a campaign should be sent. If it
|
33
|
+
# is provided the campaign will be scheduled to this time,
|
34
|
+
# otherwise campaign is sent immediately. The schedule_for
|
35
|
+
# must be in ISO date format and should be in the future.
|
36
|
+
def create(list_id, template_id, name:, from_name:, reply_to:, subject:, google_analytics:, schedule_for: nil)
|
37
|
+
options = {}
|
38
|
+
options['list_id'] = list_id
|
39
|
+
options['template_id'] = template_id
|
40
|
+
options['name'] = name
|
41
|
+
options['from_name'] = from_name
|
42
|
+
options['reply_to'] = reply_to
|
43
|
+
options['subject'] = subject
|
44
|
+
options['google_analytics'] = google_analytics
|
45
|
+
options['schedule_for'] = schedule_for unless schedule_for.nil?
|
46
|
+
|
47
|
+
self.class.post(
|
48
|
+
'',
|
49
|
+
body: options.to_json,
|
50
|
+
headers: { 'Content-Type' => 'application/json' }
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
# List campaigns
|
55
|
+
#
|
56
|
+
# GET https://api.expresspigeon.com/campaigns
|
57
|
+
#
|
58
|
+
# Returns a list of at most 1000 created campaigns, to get the next batch use from_id parameter.
|
59
|
+
#
|
60
|
+
# from_id: id from where to get the next batch,
|
61
|
+
# e.g. the last id from the previous call
|
62
|
+
# from: start of the sending period
|
63
|
+
# (UTC, example: 2013-03-16T10:00:00.000+0000)
|
64
|
+
# to: end of the sending period
|
65
|
+
# (UTC, example: 2013-03-16T20:00:00.000+0000)
|
66
|
+
def index(from_id: nil, from: nil, to: nil)
|
67
|
+
options = {}
|
68
|
+
options['from_id'] = from_id unless from_id.nil?
|
69
|
+
options['from'] = from unless from.nil?
|
70
|
+
options['to'] = to unless to.nil?
|
71
|
+
|
72
|
+
self.class.get(
|
73
|
+
'',
|
74
|
+
query: options
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Report for a single campaign
|
79
|
+
#
|
80
|
+
# GET https://api.expresspigeon.com/campaigns/{campaign_id}
|
81
|
+
def report(campaign_id)
|
82
|
+
self.class.get("/#{campaign_id}")
|
83
|
+
end
|
84
|
+
|
85
|
+
# Get opened events for campaign
|
86
|
+
#
|
87
|
+
# GET https://api.expresspigeon.com/campaigns/{campaign_id}/opened
|
88
|
+
def report_opened(campaign_id)
|
89
|
+
self.class.get("/#{campaign_id}/opened")
|
90
|
+
end
|
91
|
+
|
92
|
+
# Get clicked events for campaign
|
93
|
+
#
|
94
|
+
# GET https://api.expresspigeon.com/campaigns/{campaign_id}/clicked
|
95
|
+
def report_clicked(campaign_id)
|
96
|
+
self.class.get("/#{campaign_id}/clicked")
|
97
|
+
end
|
98
|
+
|
99
|
+
# Get bounced contacts for campaign
|
100
|
+
#
|
101
|
+
# GET https://api.expresspigeon.com/campaigns/{campaign_id}/bounced
|
102
|
+
def report_bounced(campaign_id)
|
103
|
+
self.class.get("/#{campaign_id}/bounced")
|
104
|
+
end
|
105
|
+
|
106
|
+
# Get unsubscribed contacts for campaign
|
107
|
+
#
|
108
|
+
# GET https://api.expresspigeon.com/campaigns/{campaign_id}/unsubscribed
|
109
|
+
def report_unsubscribed(campaign_id)
|
110
|
+
self.class.get("/#{campaign_id}/unsubscribed")
|
111
|
+
end
|
112
|
+
|
113
|
+
# Get spam contacts for campaign
|
114
|
+
#
|
115
|
+
# GET https://api.expresspigeon.com/campaigns/{campaign_id}/spam
|
116
|
+
def report_spam(campaign_id)
|
117
|
+
self.class.get("/#{campaign_id}/spam")
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -35,9 +35,7 @@ module ExpressPigeon
|
|
35
35
|
self.class.post(
|
36
36
|
'/',
|
37
37
|
body: options.to_json,
|
38
|
-
headers: {
|
39
|
-
'Content-Type' => 'application/json'
|
40
|
-
}
|
38
|
+
headers: { 'Content-Type' => 'application/json' }
|
41
39
|
)
|
42
40
|
end
|
43
41
|
alias_method :update, :create
|
@@ -45,7 +43,20 @@ module ExpressPigeon
|
|
45
43
|
# Delete a single contact
|
46
44
|
#
|
47
45
|
# DELETE https://api.expresspigeon.com/contacts
|
48
|
-
|
46
|
+
#
|
47
|
+
# email: contact email to be deleted
|
48
|
+
# list_id: list id to remove contact from, if not provided contact will
|
49
|
+
# be deleted from system.
|
50
|
+
def delete(email_address, list_id: nil)
|
51
|
+
options = {}
|
52
|
+
options['email'] = email_address
|
53
|
+
options['list_id'] = list_id unless list_id.nil?
|
54
|
+
|
55
|
+
self.class.delete(
|
56
|
+
'',
|
57
|
+
body: options.to_json,
|
58
|
+
headers: { 'Content-Type' => 'application/json' }
|
59
|
+
)
|
49
60
|
end
|
50
61
|
|
51
62
|
# Move contacts between lists
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module ExpressPigeon
|
2
|
+
# Messages
|
3
|
+
#
|
4
|
+
# Sending Transactional emails requires that newsletter templates for these
|
5
|
+
# emails are created prior to sending. Such template can have merge fields,
|
6
|
+
# in a format ${field_name}. This feature allows a high degree of flexibility
|
7
|
+
# for message customization.
|
8
|
+
#
|
9
|
+
# The newsletter to be sent can have a number of merge fields, with data for
|
10
|
+
# merging dynamically provided during a call.
|
11
|
+
class Messages
|
12
|
+
include HTTParty
|
13
|
+
base_uri('https://api.expresspigeon.com/messages')
|
14
|
+
debug_output(nil)
|
15
|
+
|
16
|
+
def initialize(auth_key)
|
17
|
+
self.class.headers('X-auth-key' => auth_key)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Sending a single transactional email
|
21
|
+
#
|
22
|
+
# POST https://api.expresspigeon.com/messages
|
23
|
+
#
|
24
|
+
# template_id: newsletter template id to be sent
|
25
|
+
# to: email address to send message to
|
26
|
+
# reply_to: email address tp reply to
|
27
|
+
# from: from name, such as your name or name of your organization
|
28
|
+
# subject: email message subject
|
29
|
+
# merge_fields: values for merge fields
|
30
|
+
# view_online: generates online version of sent message. We will host
|
31
|
+
# this generated message on our servers, default is false
|
32
|
+
# click_tracking: overwrites all URLs in email to point to
|
33
|
+
# http://clicks.expresspigeon.com for click tracking.
|
34
|
+
# Setting it to false will preserve all URLs intact, but
|
35
|
+
# click tracking will not be available, default is true
|
36
|
+
# suppress_address: if true suppresses insertion of sender's physical
|
37
|
+
# address in the email, default is false
|
38
|
+
def send(template_id, to:, reply_to:, from:, subject:, merge_fields: {}, view_online: nil, click_tracking: nil, suppress_address: nil)
|
39
|
+
options = {}
|
40
|
+
options['template_id'] = template_id
|
41
|
+
options['to'] = to
|
42
|
+
options['from'] = from
|
43
|
+
options['reply_to'] = reply_to
|
44
|
+
options['subject'] = subject
|
45
|
+
options['merge_fields'] = merge_fields unless merge_fields.empty?
|
46
|
+
options['view_online'] = view_online unless view_online.nil?
|
47
|
+
options['click_tracking'] = click_tracking unless click_tracking.nil?
|
48
|
+
options['suppress_address'] = suppress_address unless suppress_address.nil?
|
49
|
+
|
50
|
+
self.class.post(
|
51
|
+
'',
|
52
|
+
body: options.to_json,
|
53
|
+
headers: { 'Content-Type' => 'application/json' }
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Report for a single message
|
58
|
+
#
|
59
|
+
# GET https://api.expresspigeon.com/messages/{id}
|
60
|
+
def status(message_id)
|
61
|
+
# NOTE: This appears to be sending a valid request but the response
|
62
|
+
# can contain many status reports. The intent seems to be to
|
63
|
+
# only return the status report for the id requested. We'll pass
|
64
|
+
# along the full response for now.
|
65
|
+
self.class.get('', query: { 'id' => message_id })
|
66
|
+
end
|
67
|
+
|
68
|
+
REPORTING_PERIODS = %i(last24hours last_week last_month)
|
69
|
+
|
70
|
+
# Report for multiple messages
|
71
|
+
#
|
72
|
+
# GET https://api.expresspigeon.com/messages
|
73
|
+
def statuses(from_id: nil, start_date: nil, end_date: nil, period: nil)
|
74
|
+
options = {}
|
75
|
+
options['from_id'] = from_id unless from_id.nil?
|
76
|
+
options['start_date'] = start_date unless start_date.nil?
|
77
|
+
options['end_date'] = end_date unless end_date.nil?
|
78
|
+
options['period'] = period unless period.nil?
|
79
|
+
|
80
|
+
self.class.get('', query: options)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -1,4 +1,28 @@
|
|
1
1
|
module ExpressPigeon
|
2
|
+
# Templates
|
2
3
|
class Templates
|
4
|
+
include HTTParty
|
5
|
+
base_uri('https://api.expresspigeon.com/templates')
|
6
|
+
debug_output(nil)
|
7
|
+
|
8
|
+
def initialize(auth_key)
|
9
|
+
self.class.headers('X-auth-key' => auth_key)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Copy template
|
13
|
+
#
|
14
|
+
# POST https://api.expresspigeon.com/templates/{template_id}/copy
|
15
|
+
#
|
16
|
+
# NOTE: It is important to use only single quotes in injected HTML
|
17
|
+
def copy(template_id, name:, merge_fields: {})
|
18
|
+
self.class.post(
|
19
|
+
"/#{template_id}/copy",
|
20
|
+
body: {
|
21
|
+
name: name,
|
22
|
+
merge_fields: merge_fields
|
23
|
+
}.to_json,
|
24
|
+
headers: { 'Content-Type' => 'application/json' }
|
25
|
+
)
|
26
|
+
end
|
3
27
|
end
|
4
28
|
end
|
data/lib/express_pigeon.rb
CHANGED
@@ -1,23 +1,16 @@
|
|
1
|
-
require 'awesome_print'
|
2
|
-
|
3
|
-
# require 'net/http'
|
4
1
|
require 'json'
|
5
|
-
# require 'uri'
|
6
2
|
|
7
3
|
require 'httparty'
|
8
4
|
require 'httmultiparty'
|
9
5
|
|
10
6
|
require 'express_pigeon/version'
|
11
7
|
|
12
|
-
require 'express_pigeon/
|
8
|
+
require 'express_pigeon/auto_responders'
|
9
|
+
require 'express_pigeon/campaigns'
|
13
10
|
require 'express_pigeon/contacts'
|
14
|
-
require 'express_pigeon/
|
11
|
+
require 'express_pigeon/lists'
|
12
|
+
require 'express_pigeon/messages'
|
15
13
|
require 'express_pigeon/templates'
|
16
|
-
require 'express_pigeon/autoresponders'
|
17
|
-
|
18
|
-
AUTH_KEY = ENV['EXPRESS_PIGEON_AUTH_KEY']
|
19
|
-
ROOT = 'https://api.expresspigeon.com/'
|
20
|
-
USE_SSL = true
|
21
14
|
|
22
15
|
module ExpressPigeon
|
23
16
|
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,37 @@
|
|
1
|
+
RSpec.describe ExpressPigeon::Messages do
|
2
|
+
let(:auth_key) { ENV['EXPRESS_PIGEON_AUTH_KEY'] }
|
3
|
+
let(:client) { ExpressPigeon::Messages.new(auth_key) }
|
4
|
+
let(:test_template_id) { Integer(ENV['TEST_TEMPLATE_ID']) }
|
5
|
+
|
6
|
+
it 'sends a transactional email and gets a status report' do
|
7
|
+
response = client.send(
|
8
|
+
test_template_id,
|
9
|
+
to: 'mike+to@just3ws.com',
|
10
|
+
from: 'mike+from@just3ws',
|
11
|
+
reply_to: 'mike+reply_to@just3ws.com',
|
12
|
+
subject: 'Test sending EP transactional emails',
|
13
|
+
merge_fields: { 'test_merge_field' => 'sending transactional email via EP' })
|
14
|
+
|
15
|
+
expect(response['code']).to eq(200)
|
16
|
+
expect(response['message']).to eq('email queued')
|
17
|
+
message_id = response['id']
|
18
|
+
expect(message_id).to be_a(Fixnum)
|
19
|
+
|
20
|
+
# NOTE: Check the Inbox you configured for you Sandbox account to see the
|
21
|
+
# actual email as it was sent.
|
22
|
+
|
23
|
+
# cheating in specs and piggy-backing to test the status[es] requests
|
24
|
+
|
25
|
+
response = client.status(message_id)
|
26
|
+
expect(response.code).to eq(200)
|
27
|
+
report = response.select { |report| report['id'] == message_id }
|
28
|
+
expect(report.length).to eq(1)
|
29
|
+
expect(report.first['id']).to eq(message_id)
|
30
|
+
|
31
|
+
response = client.statuses(from_id: message_id - 1)
|
32
|
+
expect(response.code).to eq(200)
|
33
|
+
report = response.select { |report| report['id'] == message_id }
|
34
|
+
expect(report.length).to eq(1)
|
35
|
+
expect(report.first['id']).to eq(message_id)
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
RSpec.describe ExpressPigeon::Templates do
|
2
|
+
# WARN: This test requires setting up a template to use as the baseline for
|
3
|
+
# copying templates. You can use any existing template or create new
|
4
|
+
# template from scratch.
|
5
|
+
#
|
6
|
+
# Once you've selected a template to use check the URL for the template
|
7
|
+
# and extract the TEMPLATE_ID and update the `.env` file TEST_TEMPLATE_ID.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# Given the URL `https://expresspigeon.com/newsletters/compose/26013`
|
12
|
+
# Then the .env file should be set to TEST_TEMPLATE_ID=26013
|
13
|
+
#
|
14
|
+
# You will need to clean up templates created by this test manually
|
15
|
+
# as the API doesn't support deleting templates at this time
|
16
|
+
#
|
17
|
+
|
18
|
+
let(:auth_key) { ENV['EXPRESS_PIGEON_AUTH_KEY'] }
|
19
|
+
let(:client) { ExpressPigeon::Templates.new(auth_key) }
|
20
|
+
|
21
|
+
let(:test_template_id) { Integer(ENV['TEST_TEMPLATE_ID']) }
|
22
|
+
|
23
|
+
it 'copies an existing template' do
|
24
|
+
response = client.copy(test_template_id, name: 'TEST copied template', merge_fields: {
|
25
|
+
test_merge_field: 'Hello, World!'
|
26
|
+
})
|
27
|
+
|
28
|
+
# NOTE: Since we can't see the actual merge result you'll need to log in
|
29
|
+
# to your ExpressPigeon account and check the result of the merge.
|
30
|
+
|
31
|
+
expect(response['code']).to eq(200)
|
32
|
+
|
33
|
+
# If this fails then check the response. We're dependant on the behavior
|
34
|
+
# of the env for this expectation. If we run the test multiple times then
|
35
|
+
# the autoincrement for the TEMPLATE_ID is offset further. Right now just
|
36
|
+
# expect it to be an integer greater than the initial template id.
|
37
|
+
expect(response['template_id']).to be_a(Integer)
|
38
|
+
next_template_id = Integer(response['template_id'])
|
39
|
+
expect(next_template_id).to be > test_template_id
|
40
|
+
|
41
|
+
expect(response['message']).to eq('template copied successfully')
|
42
|
+
end
|
43
|
+
end
|