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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.hound.yml +3 -0
  4. data/CHANGELOG.md +15 -0
  5. data/CONTRIBUTING.md +9 -0
  6. data/Gemfile +1 -0
  7. data/Guardfile +2 -36
  8. data/README.md +57 -0
  9. data/lib/express_pigeon/auto_responders.rb +70 -0
  10. data/lib/express_pigeon/campaigns.rb +120 -0
  11. data/lib/express_pigeon/contacts.rb +15 -4
  12. data/lib/express_pigeon/messages.rb +83 -0
  13. data/lib/express_pigeon/templates.rb +24 -0
  14. data/lib/express_pigeon/version.rb +1 -1
  15. data/lib/express_pigeon.rb +4 -11
  16. data/spec/lib/express_pigeon/auto_responders_spec.rb +2 -0
  17. data/spec/lib/express_pigeon/campaigns_spec.rb +2 -0
  18. data/spec/{express_pigeon → lib/express_pigeon}/contacts_spec.rb +0 -0
  19. data/spec/{express_pigeon → lib/express_pigeon}/lists_spec.rb +0 -0
  20. data/spec/lib/express_pigeon/messages_spec.rb +37 -0
  21. data/spec/lib/express_pigeon/templates_spec.rb +43 -0
  22. data/spec/spec_helper.rb +3 -18
  23. data.tar.gz.sig +2 -2
  24. metadata +19 -22
  25. metadata.gz.sig +0 -0
  26. data/lib/express_pigeon/api/campaigns.rb +0 -62
  27. data/lib/express_pigeon/api/contacts.rb +0 -42
  28. data/lib/express_pigeon/api/lists.rb +0 -49
  29. data/lib/express_pigeon/api/messages.rb +0 -56
  30. data/lib/express_pigeon/api.rb +0 -66
  31. data/lib/express_pigeon/autoresponders.rb +0 -0
  32. data/lib/express_pigeon/meta_hash.rb +0 -20
  33. data/lib/express_pigeon/transactional_emails.rb +0 -4
  34. data/spec/express_pigeon/api/campaigns_spec.rb +0 -100
  35. data/spec/express_pigeon/api/contacts_spec.rb +0 -146
  36. data/spec/express_pigeon/api/lists_spec.rb +0 -23
  37. 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: 4fff3380b88a30f5b07051dc8706bd011766c76c
4
- data.tar.gz: 7054faf25d532a29f68e927b73572b45b3baa033
3
+ metadata.gz: dec6639b9221476fba9c7129782bcafcd0f8865d
4
+ data.tar.gz: 013b35a5e0fd1c7d6649a7bfb8e9419c148cb849
5
5
  SHA512:
6
- metadata.gz: c0c5c02f47cbd144e218ddbd0c3dfdf429aeba130b3c5c204b14c42cf8a9774945359f0c3b2585c0ee00b9aea952cfff88268feff14625984f2240382631361d
7
- data.tar.gz: ac4bd28632697812bb4135f425314bf63931464c7d253aea717e6ce939ab552c639592b7a2354112f69e286d6f152b98ea81200926b635e93a40881fbc57062b
6
+ metadata.gz: 0179a1e751bb4a2724e2ba5235bf55fb40df81e11c9783c084770748f8014ec3d9f8844ebb2eb0857e4434eee2d38ea19637f577200cca0ea3e45fb01c9740bb
7
+ data.tar.gz: 2f67667b090857f6a0c793271879262d9a438a5258ea7a7c35f6603a285165b44f96ad5c492773affa5cb701e54067c0e0ee17211aef6ab588e5f2f8b7d9b8e8
checksums.yaml.gz.sig CHANGED
Binary file
data/.hound.yml ADDED
@@ -0,0 +1,3 @@
1
+ ruby:
2
+ enabled: true
3
+ config_file: config/.rubocop.yml
data/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # 2.4.0
2
+
3
+ - Added AutoResponders
4
+
5
+ # 2.3.0
6
+
7
+ - Added Campaigns
8
+
9
+ # 2.2.0
10
+
11
+ - Added Messages aka Transactional Emails
12
+
13
+ # 2.1.0
14
+
15
+ - Added Templates
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
+ [![Create the test template](https://www.evernote.com/shard/s13/sh/75e1cdd7-456f-428a-8277-dba816463ac5/f831f96b6fe134bbd365fe979b4e7f10/deep/0/ExpressPigeon---Newsletters.png)](https://expresspigeon.com/newsletters/)
8
+
9
+ [![Configure the test template](https://www.evernote.com/shard/s13/sh/5748e40d-5620-4452-953d-36f874a69863/8fb550e3544c1ee94309c5ba45bf85a5/deep/0/ExpressPigeon---Create-Edit-Newsletter.png)](https://expresspigeon.com/newsletters/)
10
+
11
+ [![Extract the template id](https://www.evernote.com/shard/s13/sh/98ab5e45-df3d-4650-bbbb-95017b167453/f68c2c0e7158c358fe58a652aad30300/deep/0/Screenshot-12-26-14,-10-22-AM.png)](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
@@ -8,6 +8,7 @@ group :development do
8
8
  gem 'pry-byebug'
9
9
 
10
10
  gem 'rubocop'
11
+ gem 'bundler-audit'
11
12
 
12
13
  gem 'colorize'
13
14
  end
data/Guardfile CHANGED
@@ -1,49 +1,15 @@
1
- # A sample Guardfile
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
- # Note: The cmd option is now required due to the increasing number of ways
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
  [![Build Status](https://semaphoreapp.com/api/v1/projects/3d4266af-fb45-45c5-bba9-0c5e825f3e84/316403/badge.png)](https://semaphoreapp.com/just3ws/express_pigeon)
2
2
  [![Code Climate](https://codeclimate.com/github/just3ws/express_pigeon/badges/gpa.svg)](https://codeclimate.com/github/just3ws/express_pigeon)
3
+ [![Gem Version](https://badge.fury.io/rb/express_pigeon.svg)](http://badge.fury.io/rb/express_pigeon)
4
+ [![Inline docs](http://inch-ci.org/github/just3ws/express_pigeon.svg?branch=master&style=flat)](http://inch-ci.org/github/just3ws/express_pigeon)
3
5
  [![Test Coverage](https://codeclimate.com/github/just3ws/express_pigeon/badges/coverage.svg)](https://codeclimate.com/github/just3ws/express_pigeon)
4
6
 
5
7
  Another Ruby client for ExpressPigeon.
8
+
9
+ [![If you ignore the CONTRIBUTING.md then you're going to have a bad time.](https://d8izdk6bl4gbi.cloudfront.net/https://d1015h9unskp4y.cloudfront.net/attachments/ea8fd905-5069-4377-abbb-9013db3f4507/CONTRIBUTING.jpg)](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
- def delete(_email_address)
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
@@ -1,3 +1,3 @@
1
1
  module ExpressPigeon
2
- VERSION = '2.0.2'.freeze
2
+ VERSION = '2.4.0'.freeze
3
3
  end
@@ -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/lists'
8
+ require 'express_pigeon/auto_responders'
9
+ require 'express_pigeon/campaigns'
13
10
  require 'express_pigeon/contacts'
14
- require 'express_pigeon/transactional_emails'
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
@@ -0,0 +1,2 @@
1
+ RSpec.describe ExpressPigeon::AutoResponders do
2
+ end
@@ -0,0 +1,2 @@
1
+ RSpec.describe ExpressPigeon::Campaigns do
2
+ end
@@ -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