-
Notifications
You must be signed in to change notification settings - Fork 8
MT-22401: Add Email Campaigns API #119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Rabsztok
wants to merge
4
commits into
main
Choose a base branch
from
MT-22401-ruby-email-campaigns
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
210e2f2
MT-22401: Add Email Campaigns API to the Ruby SDK
Rabsztok e17f815
MT-22401: Fix YARD @return tag format for delete
Rabsztok 1c09e90
MT-22401: rename list name param to search
Rabsztok 6337865
MT-22401: correct the delete precondition to draft-only
Rabsztok File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| require 'mailtrap' | ||
|
|
||
| client = Mailtrap::Client.new(api_key: 'your-api-key') | ||
| email_campaigns = Mailtrap::EmailCampaignsAPI.new(client) | ||
|
|
||
| # Create a new Email Campaign (always created in the draft state) | ||
| email_campaign = email_campaigns.create( | ||
| name: 'Spring Sale', | ||
| domain_id: 4321, | ||
| from_display_name: 'Acme Marketing', | ||
| from_local_part: 'news', | ||
| reply_to: { | ||
| display_name: 'Acme Support', | ||
| local_part: 'support', | ||
| domain: 'acme.com' | ||
| }, | ||
| template_attributes: { subject: 'Spring is here — 30% off' } | ||
| ) | ||
| # => #<struct Mailtrap::EmailCampaign id=4567, name="Spring Sale", current_state="draft", ...> | ||
|
|
||
| # Get all Email Campaigns (paginated, newest first; filter by name) | ||
| list = email_campaigns.list(per_page: 50, search: 'Spring') | ||
| # => #<struct Mailtrap::EmailCampaignsListResponse data=[#<struct Mailtrap::EmailCampaign ...>], pagination={...}> | ||
| list.data | ||
| # => [#<struct Mailtrap::EmailCampaign id=4567, name="Spring Sale", ...>] | ||
| list.pagination | ||
| # => {:token=>1, :prev_token=>nil, :next_token=>2, ...} | ||
|
|
||
| # Get a single Email Campaign | ||
| email_campaign = email_campaigns.get(email_campaign.id) | ||
| # => #<struct Mailtrap::EmailCampaign id=4567, name="Spring Sale", ...> | ||
|
|
||
| # Update a draft Email Campaign (partial; add the design and the audience) | ||
| email_campaigns.update( | ||
| email_campaign.id, | ||
| name: 'Spring Sale (updated)', | ||
| template_attributes: { | ||
| subject: 'New subject', | ||
| body_html: '<html><body><h1>Hi {{first_name}}!</h1>' \ | ||
| '<p><a href="__unsubscribe_url__">Unsubscribe</a></p></body></html>', | ||
| merge_tags: ['first_name'] | ||
| }, | ||
| delivery_mode: 'gradual', | ||
| delivery_options: { emails_per_hour: 1000 }, | ||
| contact_list_ids: [55, 56], | ||
| contact_segment_ids: [12] | ||
| ) | ||
| # => #<struct Mailtrap::EmailCampaign id=4567, name="Spring Sale (updated)", ...> | ||
|
|
||
| # Schedule the draft Email Campaign to start sending at a future time | ||
| schedule_at = (Time.now.utc + 86_400).strftime('%Y-%m-%dT%H:%M:%S.000Z') | ||
| email_campaigns.schedule(email_campaign.id, schedule_at) | ||
| # => #<struct Mailtrap::EmailCampaign id=4567, current_state="scheduled", ...> | ||
|
|
||
| # Cancel the scheduled Email Campaign (returns it to the draft state) | ||
| email_campaigns.cancel(email_campaign.id) | ||
| # => #<struct Mailtrap::EmailCampaign id=4567, current_state="draft", ...> | ||
|
|
||
| # Reset a scheduled Email Campaign back to draft (like cancel, only a scheduled | ||
| # campaign can be reset) | ||
| email_campaigns.schedule(email_campaign.id, schedule_at) | ||
| email_campaigns.reset(email_campaign.id) | ||
| # => #<struct Mailtrap::EmailCampaign id=4567, current_state="draft", ...> | ||
|
|
||
| # Start sending the draft Email Campaign immediately | ||
| email_campaigns.start(email_campaign.id) | ||
| # => #<struct Mailtrap::EmailCampaign id=4567, current_state="started", ...> | ||
|
|
||
| # Terminate a sending Email Campaign | ||
| email_campaigns.terminate(email_campaign.id) | ||
| # => #<struct Mailtrap::EmailCampaign id=4567, current_state="terminating", ...> | ||
|
|
||
| # Get Email Campaign statistics (pass start_date/end_date to narrow the aggregation window) | ||
| email_campaigns.stats(email_campaign.id) | ||
| # => #<struct Mailtrap::EmailCampaignStats delivery_count=1450, open_count=820, delivery_rate=0.9667, ...> | ||
|
|
||
| # Delete an Email Campaign (returns nil). Only a campaign in the draft state can be | ||
| # deleted, and a started campaign never returns to draft — so delete a fresh draft. | ||
| throwaway = email_campaigns.create( | ||
| name: 'Draft to delete', | ||
| domain_id: 4321, | ||
| from_local_part: 'news', | ||
| template_attributes: { subject: 'Draft to delete' } | ||
| ) | ||
| email_campaigns.delete(throwaway.id) | ||
| # => nil | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module Mailtrap | ||
| # Data Transfer Object for Email Campaign Stats | ||
| # | ||
| # Aggregated campaign performance metrics. All counts and rates are +0+ when the | ||
| # campaign has not been started. | ||
| # @see https://api-docs.mailtrap.io/docs/mailtrap-api-docs/email-campaigns | ||
| # @attr_reader delivery_count [Integer] Number of delivered messages | ||
| # @attr_reader open_count [Integer] Number of opened messages | ||
| # @attr_reader click_count [Integer] Number of clicked messages | ||
| # @attr_reader bounce_count [Integer] Number of bounced messages | ||
| # @attr_reader unsubscription_count [Integer] Number of unsubscriptions | ||
| # @attr_reader sent_count [Integer] Number of sent messages | ||
| # @attr_reader spam_count [Integer] Number of spam complaints | ||
| # @attr_reader delivery_rate [Float] Share of sent messages that were delivered (0–1) | ||
| # @attr_reader open_rate [Float] Share of delivered messages that were opened (0–1) | ||
| # @attr_reader click_rate [Float] Share of delivered messages that were clicked (0–1) | ||
| # @attr_reader bounce_rate [Float] Share of sent messages that bounced (0–1) | ||
| # @attr_reader spam_rate [Float] Share of sent messages marked as spam (0–1) | ||
| # @attr_reader unsubscription_rate [Float] Share of delivered messages that unsubscribed (0–1) | ||
| EmailCampaignStats = Struct.new( | ||
| :delivery_count, | ||
| :open_count, | ||
| :click_count, | ||
| :bounce_count, | ||
| :unsubscription_count, | ||
| :sent_count, | ||
| :spam_count, | ||
| :delivery_rate, | ||
| :open_rate, | ||
| :click_rate, | ||
| :bounce_rate, | ||
| :spam_rate, | ||
| :unsubscription_rate, | ||
| keyword_init: true | ||
| ) | ||
|
|
||
| # Data Transfer Object for Email Campaign | ||
| # @see https://api-docs.mailtrap.io/docs/mailtrap-api-docs/email-campaigns | ||
| # @attr_reader id [Integer] The email campaign ID | ||
| # @attr_reader domain_id [Integer] ID of the sending domain used for the campaign, | ||
| # as returned by the Sending Domains endpoints | ||
| # @attr_reader domain_name [String] Name of the sending domain used for the campaign | ||
| # @attr_reader name [String] Campaign name | ||
| # @attr_reader from_local_part [String] Local part (before the @) of the From address | ||
| # @attr_reader from_display_name [String] Display name shown in the From header | ||
| # @attr_reader reply_to [Hash, nil] Reply-To address parts (+display_name+, +local_part+, +domain+) | ||
| # @attr_reader current_state [String] Lifecycle state (+draft+, +scheduled+, +started+, +queued+, | ||
| # +paused+, +terminating+, +under_review+, +finished+, +failed+, +failed_immediately+) | ||
| # @attr_reader current_state_metadata [Hash, nil] Metadata about the most recent state transition | ||
| # (+reason+, +error+, +errors+ as an array of +{message, rcpt_index}+ objects, +scheduled_at+) | ||
| # @attr_reader created_at [String] The creation timestamp | ||
| # @attr_reader updated_at [String] The last update timestamp | ||
| # @attr_reader last_started_at [String, nil] When the campaign was last started, or +nil+ | ||
| # @attr_reader last_started_at_date [String, nil] Date the campaign was last started, present only when started | ||
| # @attr_reader recipient_total_count [Integer, nil] Total number of recipients, | ||
| # or +nil+ until the audience is resolved | ||
| # @attr_reader contact_list_ids [Array<Integer>] IDs of the contact lists included in the campaign's audience | ||
| # @attr_reader contact_segment_ids [Array<Integer>] IDs of the contact segments included in the campaign's audience | ||
| # @attr_reader delivery_mode [String] How the campaign is delivered (+rapid+ or +gradual+) | ||
| # @attr_reader delivery_options [Hash, nil] Delivery throttling options (+emails_per_hour+) | ||
| # @attr_reader template [Hash, nil] The campaign template (+id+, +subject+, +merge_tags+, | ||
| # +body_html+, +body_text+; bodies are omitted on list responses) | ||
| EmailCampaign = Struct.new( | ||
| :id, | ||
| :domain_id, | ||
| :domain_name, | ||
| :name, | ||
| :from_local_part, | ||
| :from_display_name, | ||
| :reply_to, | ||
| :current_state, | ||
| :current_state_metadata, | ||
| :created_at, | ||
| :updated_at, | ||
| :last_started_at, | ||
| :last_started_at_date, | ||
| :recipient_total_count, | ||
| :contact_list_ids, | ||
| :contact_segment_ids, | ||
| :delivery_mode, | ||
| :delivery_options, | ||
| :template, | ||
| keyword_init: true | ||
| ) | ||
|
|
||
| # Response from listing email campaigns (paginated) | ||
| # @see https://api-docs.mailtrap.io/docs/mailtrap-api-docs/email-campaigns | ||
| # @attr_reader data [Array<EmailCampaign>] Page of email campaigns, newest first | ||
| # @attr_reader pagination [Hash] Page-token pagination metadata | ||
| # (+token+, +prev_token+, +next_token+, +first_url+, +prev_url+, +current_url+, +next_url+) | ||
| EmailCampaignsListResponse = Struct.new( | ||
| :data, | ||
| :pagination, | ||
| keyword_init: true | ||
| ) | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,189 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require_relative 'base_api' | ||
| require_relative 'email_campaign' | ||
|
|
||
| module Mailtrap | ||
| class EmailCampaignsAPI | ||
| include BaseAPI | ||
|
|
||
| self.supported_options = %i[ | ||
| name | ||
| domain_id | ||
| from_display_name | ||
| from_local_part | ||
| reply_to | ||
| template_attributes | ||
| delivery_mode | ||
| delivery_options | ||
| contact_list_ids | ||
| contact_segment_ids | ||
| ].freeze | ||
|
|
||
| self.response_class = EmailCampaign | ||
|
|
||
| attr_reader :client | ||
|
|
||
| # @param client [Mailtrap::Client] The client instance | ||
| def initialize(client = Mailtrap::Client.new) | ||
| @client = client | ||
| end | ||
|
|
||
| # Lists email campaigns for the account, newest first | ||
| # @param per_page [Integer, nil] Number of campaigns per page (max 100, default 50) | ||
| # @param search [String, nil] Filter campaigns by name | ||
| # @param token [Integer, nil] Page number to retrieve (page-token pagination, default 1) | ||
| # @return [EmailCampaignsListResponse] The page of campaigns and pagination metadata | ||
| # @!macro api_errors | ||
| def list(per_page: nil, search: nil, token: nil) | ||
| query_params = {} | ||
| query_params[:per_page] = per_page unless per_page.nil? | ||
| query_params[:search] = search unless search.nil? | ||
| query_params[:token] = token unless token.nil? | ||
|
|
||
| response = client.get(base_path, query_params) | ||
|
|
||
| EmailCampaignsListResponse.new( | ||
| data: Array(response[:data]).map { |item| build_entity(item, response_class) }, | ||
| pagination: response[:pagination] | ||
| ) | ||
| end | ||
|
|
||
| # Retrieves a specific email campaign | ||
| # @param email_campaign_id [Integer] The email campaign ID | ||
| # @return [EmailCampaign] Email campaign object | ||
| # @!macro api_errors | ||
| def get(email_campaign_id) | ||
| base_get(email_campaign_id) | ||
| end | ||
|
|
||
| # Creates a new email campaign in the +draft+ state | ||
| # @param [Hash] options The parameters to create | ||
| # @option options [String] :name Campaign name (required) | ||
| # @option options [Integer] :domain_id ID of the verified sending domain (required), | ||
| # as returned by the Sending Domains endpoints | ||
| # @option options [String] :from_display_name Display name shown in the From header | ||
| # @option options [String] :from_local_part Local part (before the @) of the From address (required) | ||
| # @option options [Hash] :reply_to Reply-To address parts (+display_name+, +local_part+, +domain+) | ||
| # @option options [Hash] :template_attributes Template attributes (+subject+ (required), | ||
| # +body_html+, +body_text+, +merge_tags+) | ||
| # @option options [String] :delivery_mode How the campaign is delivered (+rapid+ or +gradual+) | ||
| # @option options [Hash] :delivery_options Delivery throttling options (+emails_per_hour+), | ||
| # applies when +delivery_mode+ is +gradual+ | ||
| # @option options [Array<Integer>] :contact_list_ids IDs of contact lists to send to | ||
| # @option options [Array<Integer>] :contact_segment_ids IDs of contact segments to send to | ||
| # @return [EmailCampaign] Created email campaign | ||
| # @!macro api_errors | ||
| # @raise [ArgumentError] If invalid options are provided | ||
| def create(options) | ||
| base_create(options) | ||
| end | ||
|
|
||
| # Updates an existing +draft+ email campaign. Only the provided attributes are changed; | ||
| # +template_attributes+ sub-fields are also updated partially, in place. | ||
| # @param email_campaign_id [Integer] The email campaign ID | ||
| # @param [Hash] options The parameters to update; accepts the same fields as {#create} | ||
| # @option options [String] :name Campaign name | ||
| # @option options [Integer] :domain_id ID of the verified sending domain, | ||
| # as returned by the Sending Domains endpoints | ||
| # @option options [String] :from_display_name Display name shown in the From header | ||
| # @option options [String] :from_local_part Local part (before the @) of the From address | ||
| # @option options [Hash] :reply_to Reply-To address parts (+display_name+, +local_part+, +domain+) | ||
| # @option options [Hash] :template_attributes Template attributes (+subject+, +body_html+, | ||
| # +body_text+, +merge_tags+) | ||
| # @option options [String] :delivery_mode How the campaign is delivered (+rapid+ or +gradual+) | ||
| # @option options [Hash] :delivery_options Delivery throttling options (+emails_per_hour+), | ||
| # applies when +delivery_mode+ is +gradual+ | ||
| # @option options [Array<Integer>] :contact_list_ids IDs of contact lists to send to | ||
| # @option options [Array<Integer>] :contact_segment_ids IDs of contact segments to send to | ||
| # @return [EmailCampaign] Updated email campaign | ||
| # @!macro api_errors | ||
| # @raise [ArgumentError] If invalid options are provided | ||
| def update(email_campaign_id, options) | ||
| base_update(email_campaign_id, options) | ||
| end | ||
|
|
||
| # Deletes an email campaign. Only a campaign in the +draft+ state can be deleted. | ||
| # @param email_campaign_id [Integer] The email campaign ID | ||
| # @return [nil] | ||
| # @!macro api_errors | ||
| def delete(email_campaign_id) | ||
| base_delete(email_campaign_id) | ||
| end | ||
|
|
||
| # Starts sending a +draft+ campaign immediately | ||
| # @param email_campaign_id [Integer] The email campaign ID | ||
| # @return [EmailCampaign] The started email campaign | ||
| # @!macro api_errors | ||
| def start(email_campaign_id) | ||
| perform_action(email_campaign_id, :start) | ||
| end | ||
|
|
||
| # Schedules a +draft+ campaign to start sending at a future time. | ||
| # The time is reported back in +current_state_metadata.scheduled_at+. | ||
| # @param email_campaign_id [Integer] The email campaign ID | ||
| # @param datetime [String] When to send the campaign (ISO 8601); must be in the future | ||
| # and no more than 1 month ahead | ||
| # @return [EmailCampaign] The scheduled email campaign | ||
| # @!macro api_errors | ||
| def schedule(email_campaign_id, datetime) | ||
| perform_action(email_campaign_id, :schedule, { datetime: }) | ||
| end | ||
|
|
||
| # Cancels a +scheduled+ campaign, returning it to the +draft+ state | ||
| # @param email_campaign_id [Integer] The email campaign ID | ||
| # @return [EmailCampaign] The cancelled email campaign | ||
| # @!macro api_errors | ||
| def cancel(email_campaign_id) | ||
| perform_action(email_campaign_id, :cancel) | ||
| end | ||
|
|
||
| # Terminates a campaign that is currently sending (+started+, +queued+, or +paused+), | ||
| # aborting the in-flight send | ||
| # @param email_campaign_id [Integer] The email campaign ID | ||
| # @return [EmailCampaign] The terminated email campaign | ||
| # @!macro api_errors | ||
| def terminate(email_campaign_id) | ||
| perform_action(email_campaign_id, :terminate) | ||
| end | ||
|
|
||
| # Resets a +scheduled+ campaign back to the +draft+ state | ||
| # @param email_campaign_id [Integer] The email campaign ID | ||
| # @return [EmailCampaign] The reset email campaign | ||
| # @!macro api_errors | ||
| def reset(email_campaign_id) | ||
| perform_action(email_campaign_id, :reset) | ||
| end | ||
|
|
||
| # Retrieves aggregated performance statistics for an email campaign. | ||
| # By default statistics are aggregated since the campaign was last started. | ||
| # @param email_campaign_id [Integer] The email campaign ID | ||
| # @param start_date [String, nil] Start of the aggregation window (inclusive), +YYYY-MM-DD+ | ||
| # @param end_date [String, nil] End of the aggregation window (inclusive), +YYYY-MM-DD+ | ||
| # @return [EmailCampaignStats] Aggregated campaign statistics | ||
| # @!macro api_errors | ||
| def stats(email_campaign_id, start_date: nil, end_date: nil) | ||
| query_params = {} | ||
| query_params[:start_date] = start_date unless start_date.nil? | ||
| query_params[:end_date] = end_date unless end_date.nil? | ||
|
|
||
| response = client.get("#{base_path}/#{email_campaign_id}/stats", query_params) | ||
| build_entity(response[:data], EmailCampaignStats) | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def perform_action(email_campaign_id, action, body = nil) | ||
| response = client.post("#{base_path}/#{email_campaign_id}/#{action}", body) | ||
| handle_response(response) | ||
| end | ||
|
|
||
| def base_path | ||
| '/api/email_campaigns' | ||
| end | ||
|
|
||
| def handle_response(response) | ||
| build_entity(response[:data], response_class) | ||
| end | ||
| end | ||
| end |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.