Skip to content

Add Email Templates API support #44

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## [2.4.0] - 2025-05-12

- Added Email Templates API support
- Introduced template management methods on `Mailtrap::Client`

## [2.3.0] - 2025-03-06

- Drop Ruby 3.0 support
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
mailtrap (2.3.0)
mailtrap (2.4.0)

GEM
remote: https://rubygems.org/
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,16 @@ Refer to the [`examples`](examples) folder for more examples.

- [Full](examples/full.rb)
- [Email template](examples/email_template.rb)
- [Email templates management](examples/email_templates.rb)
- [ActionMailer](examples/action_mailer.rb)

### Email Templates management

```ruby
client = Mailtrap::Client.new(api_key: 'your-api-key')
templates = client.list_templates(account_id: 1)
```

### Content-Transfer-Encoding

`mailtrap` gem uses Mailtrap API to send emails. Mailtrap API does not try to
Expand Down
26 changes: 26 additions & 0 deletions examples/email_templates.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require 'mailtrap'

client = Mailtrap::Client.new(api_key: 'your-api-key')
account_id = 1

# list templates
client.list_templates(account_id:)

# create template
created = client.create_template(
account_id:,
name: 'Newsletter Template',
subject: 'Subject',
category: 'Newsletter',
body_html: '<div>Hello</div>'
)

# update template
client.update_template(
account_id:,
email_template_id: created[:id],
name: 'Updated Template'
)

# delete template
client.destroy_template(account_id:, email_template_id: created[:id])
1 change: 1 addition & 0 deletions lib/mailtrap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
require_relative 'mailtrap/mail'
require_relative 'mailtrap/errors'
require_relative 'mailtrap/version'
require_relative 'mailtrap/client'

module Mailtrap; end
65 changes: 64 additions & 1 deletion lib/mailtrap/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
require 'uri'

module Mailtrap
class Client
class Client # rubocop:disable Metrics/ClassLength
SENDING_API_HOST = 'send.api.mailtrap.io'
BULK_SENDING_API_HOST = 'bulk.api.mailtrap.io'
SANDBOX_API_HOST = 'sandbox.api.mailtrap.io'
TEMPLATES_API_HOST = 'mailtrap.io'
API_PORT = 443

attr_reader :api_key, :api_host, :api_port, :bulk, :sandbox, :inbox_id
Expand Down Expand Up @@ -55,6 +56,27 @@ def send(mail)
handle_response(response)
end

def list_templates(account_id:)
template_request(:get, "/api/accounts/#{account_id}/email_templates")
end

def create_template(account_id:, **params)
template_request(:post, "/api/accounts/#{account_id}/email_templates", params)
end

def update_template(account_id:, email_template_id:, **params)
template_request(
:patch,
"/api/accounts/#{account_id}/email_templates/#{email_template_id}",
params
)
end

def destroy_template(account_id:, email_template_id:)
template_request(:delete, "/api/accounts/#{account_id}/email_templates/#{email_template_id}")
true
end

private

def select_api_host(bulk:, sandbox:)
Expand All @@ -77,6 +99,10 @@ def http_client
@http_client ||= Net::HTTP.new(api_host, api_port).tap { |client| client.use_ssl = true }
end

def template_http_client
@template_http_client ||= Net::HTTP.new(TEMPLATES_API_HOST, api_port).tap { |client| client.use_ssl = true }
end

def post_request(path, body)
request = Net::HTTP::Post.new(path)
request.body = body
Expand Down Expand Up @@ -113,5 +139,42 @@ def handle_response(response) # rubocop:disable Metrics/AbcSize, Metrics/Cycloma
def json_response(body)
JSON.parse(body, symbolize_names: true)
end

def template_request(http_method, path, body = nil) # rubocop:disable Metrics/MethodLength
request_class = {
get: Net::HTTP::Get,
post: Net::HTTP::Post,
patch: Net::HTTP::Patch,
delete: Net::HTTP::Delete
}.fetch(http_method)

request = request_class.new(path)
request['Authorization'] = "Bearer #{api_key}"
request['User-Agent'] = 'mailtrap-ruby (https://github.com/railsware/mailtrap-ruby)'
if body
request['Content-Type'] = 'application/json'
request.body = JSON.generate(body)
end

response = template_http_client.request(request)
handle_template_response(response)
end # rubocop:enable Metrics/MethodLength

def handle_template_response(response) # rubocop:disable Metrics/MethodLength
case response
when Net::HTTPNoContent
true
when Net::HTTPSuccess
json_response(response.body)
when Net::HTTPUnauthorized
raise Mailtrap::AuthorizationError, json_response(response.body)[:errors]
when Net::HTTPForbidden
raise Mailtrap::RejectionError, json_response(response.body)[:errors]
when Net::HTTPClientError, Net::HTTPServerError
raise Mailtrap::Error, json_response(response.body)[:errors]
else
raise Mailtrap::Error, ["unexpected status code=#{response.code}"]
end
end # rubocop:enable Metrics/MethodLength
end
end
2 changes: 1 addition & 1 deletion lib/mailtrap/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Mailtrap
VERSION = '2.3.0'
VERSION = '2.4.0'
end
64 changes: 64 additions & 0 deletions spec/mailtrap/client_templates_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# frozen_string_literal: true

RSpec.describe Mailtrap::Client do
subject(:client) { described_class.new(api_key:) }

let(:api_key) { 'correct-api-key' }
let(:account_id) { 123 }
let(:template_id) { 456 }

def stub_api(method, path, status:, body: nil)
stub = stub_request(method, "https://mailtrap.io#{path}")
.to_return(status:, body:)
yield
expect(stub).to have_been_requested
end

describe '#list_templates' do
it 'returns templates list' do
stub_api(:get, "/api/accounts/#{account_id}/email_templates", status: 200, body: '[{"id":1}]') do
expect(client.list_templates(account_id:)).to eq([{ id: 1 }])
end
end
end

describe '#create_template' do
let(:params) { { name: 'Test', subject: 'Subj', category: 'Promotion', body_html: '<div>body</div>' } }

it 'sends POST request with JSON body' do
stub = stub_request(:post, "https://mailtrap.io/api/accounts/#{account_id}/email_templates")
.with(body: params.to_json)
.to_return(status: 201, body: '{"id":2}')
expect(client.create_template(account_id:, **params)).to eq({ id: 2 })
expect(stub).to have_been_requested
end
end

describe '#update_template' do
it 'sends PATCH request with JSON body' do # rubocop:disable RSpec/ExampleLength
stub = stub_request(:patch, "https://mailtrap.io/api/accounts/#{account_id}/email_templates/#{template_id}")
.with(body: { name: 'Updated' }.to_json)
.to_return(status: 200, body: '{"id":2,"name":"Updated"}')
expect(
client.update_template(account_id:, email_template_id: template_id, name: 'Updated')
).to eq({ id: 2, name: 'Updated' })
expect(stub).to have_been_requested
end
end

describe '#destroy_template' do
it 'sends DELETE request' do
stub_api(:delete, "/api/accounts/#{account_id}/email_templates/#{template_id}", status: 204) do
expect(client.destroy_template(account_id:, email_template_id: template_id)).to be true
end
end
end

describe 'error handling' do
it 'raises authorization error' do
stub_api(:get, "/api/accounts/#{account_id}/email_templates", status: 401, body: '{"errors":["Unauthorized"]}') do
expect { client.list_templates(account_id:) }.to raise_error(Mailtrap::AuthorizationError)
end
end
end
end