diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2e20365..86c6aa2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,6 +9,8 @@ jobs: build: runs-on: ubuntu-latest name: Ruby ${{ matrix.ruby }} + env: + BUNDLE_PATH: vendor/bundle strategy: matrix: ruby: diff --git a/CHANGELOG.md b/CHANGELOG.md index c6bcd72..3df6ac5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- Add Sending Domains API + ## [2.6.0] - 2026-01-27 - Add Inboxes API - Add Projects API diff --git a/README.md b/README.md index b92b8c1..335d5f8 100644 --- a/README.md +++ b/README.md @@ -173,9 +173,10 @@ mail(delivery_method: :mailtrap_bulk) Refer to the [`examples`](examples) folder for more examples: Email API: -- Full email sending – [`full.rb`](examples/full.rb) -- Batch sending – [`batch.rb`](examples/batch.rb) +- Full Email Sending – [`full.rb`](examples/full.rb) +- Batch Sending – [`batch.rb`](examples/batch.rb) +- Sending Domains API – [`sending_domains_api.rb`](examples/sending_domains_api.rb) Email Sandbox (Testing): @@ -184,7 +185,7 @@ Email Sandbox (Testing): Contact management: -- Contacts CRUD & listing – [`contacts_api.rb`](examples/contacts_api.rb) +- Contacts CRUD & Listing – [`contacts_api.rb`](examples/contacts_api.rb) General API: diff --git a/examples/sending_domains_api.rb b/examples/sending_domains_api.rb new file mode 100644 index 0000000..49eb53b --- /dev/null +++ b/examples/sending_domains_api.rb @@ -0,0 +1,25 @@ +require 'mailtrap' + +account_id = 3229 +client = Mailtrap::Client.new(api_key: 'your-api-key') +sending_domains = Mailtrap::SendingDomainsAPI.new(account_id, client) + +# Create new Sending Domain +sending_domain = sending_domains.create(domain_name: 'example.com') +# => # + +# Get all Sending Domains +sending_domains.list +# => [#] + +# Get sending domain +sending_domain = sending_domains.get(sending_domain.id) +# => # + +# Send setup email +sending_domains.send_setup_instructions(sending_domain.id, 'jonathan@mail.com') +# => nil + +# Delete sending domain +sending_domains.delete(sending_domain.id) +# => nil diff --git a/lib/mailtrap.rb b/lib/mailtrap.rb index 37f1cdd..728d0c3 100644 --- a/lib/mailtrap.rb +++ b/lib/mailtrap.rb @@ -12,6 +12,7 @@ require_relative 'mailtrap/suppressions_api' require_relative 'mailtrap/projects_api' require_relative 'mailtrap/inboxes_api' +require_relative 'mailtrap/sending_domains_api' module Mailtrap # @!macro api_errors diff --git a/lib/mailtrap/sending_domain.rb b/lib/mailtrap/sending_domain.rb new file mode 100644 index 0000000..c09f776 --- /dev/null +++ b/lib/mailtrap/sending_domain.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Mailtrap + # Data Transfer Object for Sending Domain + # @see https://docs.mailtrap.io/developers/management/sending-domains + # @attr_reader id [Integer] The sending domain ID + # @attr_reader domain_name [String] The sending domain name + # @attr_reader demo [Boolean] Whether the sending domain is a demo domain + # @attr_reader compliance_status [String] The compliance status of the sending domain + # @attr_reader dns_verified [Boolean] Whether the DNS records are verified + # @attr_reader dns_verified_at [String, nil] The timestamp when DNS was verified + # @attr_reader dns_records [Array] The DNS records for the sending domain + # @attr_reader open_tracking_enabled [Boolean] Whether open tracking is enabled + # @attr_reader click_tracking_enabled [Boolean] Whether click tracking is enabled + # @attr_reader auto_unsubscribe_link_enabled [Boolean] Whether auto unsubscribe link is enabled + # @attr_reader custom_domain_tracking_enabled [Boolean] Whether custom domain tracking is enabled + # @attr_reader health_alerts_enabled [Boolean] Whether health alerts are enabled + # @attr_reader critical_alerts_enabled [Boolean] Whether critical alerts are enabled + # @attr_reader alert_recipient_email [String, nil] The email address for alert recipients + # @attr_reader permissions [Hash] The permissions for the sending domain + # + SendingDomain = Struct.new( + :id, + :domain_name, + :demo, + :compliance_status, + :dns_verified, + :dns_verified_at, + :dns_records, + :open_tracking_enabled, + :click_tracking_enabled, + :auto_unsubscribe_link_enabled, + :custom_domain_tracking_enabled, + :health_alerts_enabled, + :critical_alerts_enabled, + :alert_recipient_email, + :permissions, + :created_at, + :updated_at, + keyword_init: true + ) +end diff --git a/lib/mailtrap/sending_domains_api.rb b/lib/mailtrap/sending_domains_api.rb new file mode 100644 index 0000000..a87e84c --- /dev/null +++ b/lib/mailtrap/sending_domains_api.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require_relative 'base_api' +require_relative 'sending_domain' + +module Mailtrap + class SendingDomainsAPI + include BaseAPI + + self.supported_options = %i[domain_name] + + self.response_class = SendingDomain + + # Lists all sending domains for the account + # @return [Array] Array of sending domains + # @!macro api_errors + def list + response = client.get(base_path) + response[:data].map { |item| handle_response(item) } + end + + # Retrieves a specific sending domain + # @param domain_id [Integer] The sending domain ID + # @return [SendingDomain] Sending domain object + # @!macro api_errors + def get(domain_id) + base_get(domain_id) + end + + # Creates a new sending domain + # @param [Hash] options The parameters to create + # @option options [String] :domain_name The sending domain name + # @return [SendingDomain] Created sending domain + # @!macro api_errors + # @raise [ArgumentError] If invalid options are provided + def create(options) + base_create(options) + end + + # Deletes a sending domain + # @param domain_id [Integer] The sending domain ID + # @return nil + # @!macro api_errors + def delete(domain_id) + base_delete(domain_id) + end + + # Email DNS configuration instructions for the sending domain + # @param domain_id [Integer] The sending domain ID + # @param email [String] The email for instructions + # @return nil + # @!macro api_errors + def send_setup_instructions(domain_id:, email:) + client.post("#{base_path}/#{domain_id}/send_setup_instructions", email:) + end + + private + + def base_path + "/api/accounts/#{account_id}/sending_domains" + end + + def wrap_request(options) + { sending_domain: options } + end + end +end diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_create/maps_response_data_to_sending_domain_object.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_create/maps_response_data_to_sending_domain_object.yml new file mode 100644 index 0000000..6d795a8 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_create/maps_response_data_to_sending_domain_object.yml @@ -0,0 +1,80 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/accounts/1111111/sending_domains + body: + encoding: UTF-8 + string: '{"sending_domain":{"domain_name":"mailtrappio.com"}}' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 04 Jan 2026 16:27:07 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + Vary: + - Accept + - Accept-Encoding + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"0c5da75876ea174e9cd60e204537d3d1" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - d2500d88-9657-4bd7-8d41-8a80d63e870f + X-Runtime: + - '0.075491' + X-Cloud-Trace-Context: + - d3ef2277158a46b3c6bd0be3a0b6e040;o=0 + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 9b8c19fcecbe9844-FRA + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '{"domain_name":"mailtrappio.com","open_tracking_enabled":true,"click_tracking_enabled":false,"auto_unsubscribe_link_enabled":false,"custom_domain_tracking_enabled":false,"health_alerts_enabled":true,"critical_alerts_enabled":true,"alert_recipient_email":null,"demo":false,"id":943758,"dns_records":[{"key":"verification","domain":"mt07.mailtrappio.com","type":"CNAME","value":"smtp.mailtrap.live","status":"missing","name":"mt07"},{"key":"dkim1","domain":"rwmt1._domainkey.mailtrappio.com","type":"CNAME","value":"rwmt1.dkim.smtp.mailtrap.live","status":"missing","name":"rwmt1._domainkey"},{"key":"dkim2","domain":"rwmt2._domainkey.mailtrappio.com","type":"CNAME","value":"rwmt2.dkim.smtp.mailtrap.live","status":"missing","name":"rwmt2._domainkey"},{"key":"dmarc","name":"_dmarc"},{"key":"link_verification","domain":"mt-link.mailtrappio.com","type":"CNAME","value":"t.mailtrap.live","status":"missing","name":"mt-link"}],"dns_verified":false,"dns_verified_at":"","permissions":{"can_read":true,"can_update":true,"can_destroy":true},"compliance_status":"unverified_dns"}' + recorded_at: Sun, 04 Jan 2026 16:27:07 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_create/when_API_returns_an_error/raises_a_Mailtrap_Error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_create/when_API_returns_an_error/raises_a_Mailtrap_Error.yml new file mode 100644 index 0000000..ba13177 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_create/when_API_returns_an_error/raises_a_Mailtrap_Error.yml @@ -0,0 +1,78 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/accounts/1111111/sending_domains + body: + encoding: UTF-8 + string: '{"sending_domain":{"domain_name":""}}' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 422 + message: Unprocessable Entity + headers: + Date: + - Sun, 04 Jan 2026 16:27:59 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '118' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Cache-Control: + - no-cache + X-Request-Id: + - 9c5aa14d-b3c7-4c7b-8848-9dcb51cfa888 + X-Runtime: + - '0.044359' + X-Cloud-Trace-Context: + - 8086e5880b4c42658ff1eb5c747366fb;o=3 + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 9b8c1b4128a1db9d-FRA + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"errors":{"base":["Validation failed: Domain name can''t be blank, + Tracking domain name is not a valid domain name"]}}' + recorded_at: Sun, 04 Jan 2026 16:27:59 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_delete/returns_deleted_sending_domain_data.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_delete/returns_deleted_sending_domain_data.yml new file mode 100644 index 0000000..92c22d3 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_delete/returns_deleted_sending_domain_data.yml @@ -0,0 +1,71 @@ +--- +http_interactions: +- request: + method: delete + uri: https://mailtrap.io/api/accounts/1111111/sending_domains/943758 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 204 + message: No Content + headers: + Date: + - Sun, 04 Jan 2026 16:34:38 GMT + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Cache-Control: + - no-cache + X-Request-Id: + - ff7846c7-3f5b-45ee-a3e6-39018ed3fa36 + X-Runtime: + - '0.044293' + X-Cloud-Trace-Context: + - a892d9821b744c068e28f8bb66ccf9ec;o=0 + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 9b8c24f8fdccd2be-FRA + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '' + recorded_at: Sun, 04 Jan 2026 16:34:38 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_delete/when_sending_domain_does_not_exist/raises_not_found_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_delete/when_sending_domain_does_not_exist/raises_not_found_error.yml new file mode 100644 index 0000000..45480b9 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_delete/when_sending_domain_does_not_exist/raises_not_found_error.yml @@ -0,0 +1,77 @@ +--- +http_interactions: +- request: + method: delete + uri: https://mailtrap.io/api/accounts/1111111/sending_domains/-1 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 404 + message: Not Found + headers: + Date: + - Sun, 04 Jan 2026 16:34:38 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '148' + Cache-Control: + - no-cache + X-Request-Id: + - e00a50ba-5750-4d20-9304-42f3f93706a8 + X-Runtime: + - '0.016651' + X-Cloud-Trace-Context: + - '0924f50da62d40458aaba48390504732;o=0' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 9b8c24fc2c91dca0-FRA + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '{"error":"Not Found"}' + recorded_at: Sun, 04 Jan 2026 16:34:38 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_get/maps_response_data_to_SendingDomain_object.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_get/maps_response_data_to_SendingDomain_object.yml new file mode 100644 index 0000000..93b9226 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_get/maps_response_data_to_SendingDomain_object.yml @@ -0,0 +1,80 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/accounts/1111111/sending_domains/617882 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Sun, 04 Jan 2026 16:24:01 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + Vary: + - Accept + - Accept-Encoding + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"599af6a879c389fe0410607dfae4e4ef" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - cb009637-a8f0-41bf-8373-affab5119dfa + X-Runtime: + - '0.036453' + X-Cloud-Trace-Context: + - 0c408f022cf54b76c3a160c48508f34d;o=0 + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 9b8c15718a2fd206-FRA + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '{"domain_name":"demomailtrap.co","open_tracking_enabled":true,"click_tracking_enabled":false,"auto_unsubscribe_link_enabled":false,"custom_domain_tracking_enabled":false,"health_alerts_enabled":true,"critical_alerts_enabled":true,"alert_recipient_email":null,"demo":true,"id":617882,"dns_records":[{"key":"verification","domain":"demo.demomailtrap.co","type":"CNAME","value":"smtp.mailtrap.live","status":"pass","name":"demo"},{"key":"dkim1","domain":"rwmt1._domainkey.demomailtrap.co","type":"CNAME","value":"rwmt1.dkim.smtp.mailtrap.live","status":"pass","name":"rwmt1._domainkey"},{"key":"dkim2","domain":"rwmt2._domainkey.demomailtrap.co","type":"CNAME","value":"rwmt2.dkim.smtp.mailtrap.live","status":"pass","name":"rwmt2._domainkey"},{"key":"dmarc","name":"_dmarc"},{"key":"link_verification","domain":"mt-link.demomailtrap.co","type":"CNAME","value":"t.mailtrap.live","status":"pass","name":"mt-link"}],"dns_verified":true,"dns_verified_at":"2025-05-21T14:04:52.363Z","permissions":{"can_read":true,"can_update":true,"can_destroy":true},"compliance_status":"demo"}' + recorded_at: Sun, 04 Jan 2026 16:24:01 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_get/when_sending_domain_does_not_exist/raises_not_found_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_get/when_sending_domain_does_not_exist/raises_not_found_error.yml new file mode 100644 index 0000000..f8d7936 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_get/when_sending_domain_does_not_exist/raises_not_found_error.yml @@ -0,0 +1,77 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/accounts/1111111/sending_domains/-1 + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 404 + message: Not Found + headers: + Date: + - Sun, 04 Jan 2026 16:24:02 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '148' + Cache-Control: + - no-cache + X-Request-Id: + - 0b7c9190-ce7f-4a83-b2c9-2b3c416747a5 + X-Runtime: + - '0.016330' + X-Cloud-Trace-Context: + - 6497608d8e4442a3cb0403a7a8635ef8;o=0 + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 9b8c1574b858e98a-FRA + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '{"error":"Not Found"}' + recorded_at: Sun, 04 Jan 2026 16:24:02 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_list/maps_response_data_to_SendingDomain_objects.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_list/maps_response_data_to_SendingDomain_objects.yml new file mode 100644 index 0000000..0b695fa --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_list/maps_response_data_to_SendingDomain_objects.yml @@ -0,0 +1,80 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/accounts/1111111/sending_domains + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Sat, 03 Jan 2026 22:53:34 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + Vary: + - Accept + - Accept-Encoding + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"4e212b2ed54fe51b05a73fd9a42b1f60" + Cache-Control: + - max-age=0, private, must-revalidate + X-Request-Id: + - d11fce57-074f-4aea-84a4-68aa463f5473 + X-Runtime: + - '0.035447' + X-Cloud-Trace-Context: + - fe0756e8b8cf4a058f135f0aaaaca32f;o=0 + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 9b8612b32ded4d6d-FRA + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '{"data":[{"domain_name":"demomailtrap.co","open_tracking_enabled":true,"click_tracking_enabled":false,"auto_unsubscribe_link_enabled":false,"custom_domain_tracking_enabled":false,"health_alerts_enabled":true,"critical_alerts_enabled":true,"alert_recipient_email":null,"demo":true,"id":617882,"dns_records":[{"key":"verification","domain":"demo.demomailtrap.co","type":"CNAME","value":"smtp.mailtrap.live","status":"pass","name":"demo"},{"key":"dkim1","domain":"rwmt1._domainkey.demomailtrap.co","type":"CNAME","value":"rwmt1.dkim.smtp.mailtrap.live","status":"pass","name":"rwmt1._domainkey"},{"key":"dkim2","domain":"rwmt2._domainkey.demomailtrap.co","type":"CNAME","value":"rwmt2.dkim.smtp.mailtrap.live","status":"pass","name":"rwmt2._domainkey"},{"key":"dmarc","name":"_dmarc"},{"key":"link_verification","domain":"mt-link.demomailtrap.co","type":"CNAME","value":"t.mailtrap.live","status":"pass","name":"mt-link"}],"dns_verified":true,"dns_verified_at":"2025-05-21T14:04:52.363Z","permissions":{"can_read":true,"can_update":true,"can_destroy":true},"compliance_status":"demo"}]}' + recorded_at: Sat, 03 Jan 2026 22:53:34 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_list/when_api_key_is_incorrect/raises_authorization_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_list/when_api_key_is_incorrect/raises_authorization_error.yml new file mode 100644 index 0000000..d38f617 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_list/when_api_key_is_incorrect/raises_authorization_error.yml @@ -0,0 +1,79 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/accounts/1111111/sending_domains + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 401 + message: Unauthorized + headers: + Date: + - Sat, 03 Jan 2026 22:53:35 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '31' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Www-Authenticate: + - Token realm="Application" + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Cache-Control: + - no-cache + X-Request-Id: + - 742028ce-9e82-4944-970c-a5144171e1ed + X-Runtime: + - '0.006799' + X-Cloud-Trace-Context: + - 6b82378ed8b248f88625946d441a4c57;o=0 + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 9b8612b47f26913d-FRA + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"error":"Incorrect API token"}' + recorded_at: Sat, 03 Jan 2026 22:53:35 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_send_setup_instructions/returns_nil.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_send_setup_instructions/returns_nil.yml new file mode 100644 index 0000000..6bd929a --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_send_setup_instructions/returns_nil.yml @@ -0,0 +1,71 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/accounts/1111111/sending_domains/943758/send_setup_instructions + body: + encoding: UTF-8 + string: '{"email":"example@railsware.com"}' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 204 + message: No Content + headers: + Date: + - Sun, 04 Jan 2026 16:34:05 GMT + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Cache-Control: + - no-cache + X-Request-Id: + - e6339efe-a22c-4dad-9724-eee8452c9cca + X-Runtime: + - '0.057744' + X-Cloud-Trace-Context: + - 403a86d6578f4f87caf983c7c84f3f60;o=0 + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 9b8c242ccf73a079-FRA + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '' + recorded_at: Sun, 04 Jan 2026 16:34:05 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_send_setup_instructions/when_sending_domain_does_not_exist/raises_not_found_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_send_setup_instructions/when_sending_domain_does_not_exist/raises_not_found_error.yml new file mode 100644 index 0000000..847a141 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SendingDomainsAPI/_send_setup_instructions/when_sending_domain_does_not_exist/raises_not_found_error.yml @@ -0,0 +1,77 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/accounts/1111111/sending_domains/-1/send_setup_instructions + body: + encoding: UTF-8 + string: '{"email":"example@railsware.com"}' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 404 + message: Not Found + headers: + Date: + - Sun, 04 Jan 2026 16:34:05 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '148' + Cache-Control: + - no-cache + X-Request-Id: + - 521fd5b1-ef92-4c5b-9833-705ea0989e3d + X-Runtime: + - '0.014296' + X-Cloud-Trace-Context: + - 27f7fea3f645478488361be8cc7262d4;o=0 + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Cf-Ray: + - 9b8c24301fe6363f-FRA + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '{"error":"Not Found"}' + recorded_at: Sun, 04 Jan 2026 16:34:05 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/mailtrap/sending_domain_spec.rb b/spec/mailtrap/sending_domain_spec.rb new file mode 100644 index 0000000..9b7859e --- /dev/null +++ b/spec/mailtrap/sending_domain_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::SendingDomain do + describe '#initialize' do + subject(:sending_domain) { described_class.new(attributes) } + + let(:attributes) do + { + id: '123456', + domain_name: 'My Sending Domain', + dns_verified: false, + compliance_status: 'pending', + created_at: '2024-01-01T00:00:00Z', + updated_at: '2024-01-02T00:00:00Z' + } + end + + it 'creates a sending domain with all attributes' do + expect(sending_domain).to have_attributes( + id: '123456', + domain_name: 'My Sending Domain', + dns_verified: false, + compliance_status: 'pending', + created_at: '2024-01-01T00:00:00Z', + updated_at: '2024-01-02T00:00:00Z' + ) + end + end +end diff --git a/spec/mailtrap/sending_domains_api_spec.rb b/spec/mailtrap/sending_domains_api_spec.rb new file mode 100644 index 0000000..55c2d03 --- /dev/null +++ b/spec/mailtrap/sending_domains_api_spec.rb @@ -0,0 +1,187 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::SendingDomainsAPI, :vcr do + subject(:sending_domains_api) { described_class.new(account_id, client) } + + let(:account_id) { ENV.fetch('MAILTRAP_ACCOUNT_ID', 1_111_111) } + let(:client) { Mailtrap::Client.new(api_key: ENV.fetch('MAILTRAP_API_KEY', 'local-api-key')) } + + describe '#list' do + subject(:list) { sending_domains_api.list } + + it 'maps response data to SendingDomain objects' do + expect(list).to all(be_a(Mailtrap::SendingDomain)) + end + + context 'when api key is incorrect' do + let(:client) { Mailtrap::Client.new(api_key: 'incorrect-api-key') } + + it 'raises authorization error' do + expect { list }.to raise_error do |error| + expect(error).to be_a(Mailtrap::AuthorizationError) + expect(error.message).to include('Incorrect API token') + expect(error.messages.any? { |msg| msg.include?('Incorrect API token') }).to be true + end + end + end + end + + describe '#get' do + subject(:get) { sending_domains_api.get(sending_domain_id) } + + let(:sending_domain_id) { 617_882 } + + it 'maps response data to SendingDomain object' do + expect(get).to be_a(Mailtrap::SendingDomain) + + expect(get).to match_struct( + id: 617_882, + domain_name: 'demomailtrap.co', + demo: true, + compliance_status: 'demo', + dns_verified: true, + dns_verified_at: '2025-05-21T14:04:52.363Z', + dns_records: + [ + { key: 'verification', domain: 'demo.demomailtrap.co', type: 'CNAME', value: 'smtp.mailtrap.live', + status: 'pass', name: 'demo' }, + { key: 'dkim1', domain: 'rwmt1._domainkey.demomailtrap.co', type: 'CNAME', + value: 'rwmt1.dkim.smtp.mailtrap.live', status: 'pass', name: 'rwmt1._domainkey' }, + { key: 'dkim2', domain: 'rwmt2._domainkey.demomailtrap.co', type: 'CNAME', + value: 'rwmt2.dkim.smtp.mailtrap.live', status: 'pass', name: 'rwmt2._domainkey' }, + { key: 'dmarc', name: '_dmarc' }, + { key: 'link_verification', domain: 'mt-link.demomailtrap.co', type: 'CNAME', value: 't.mailtrap.live', + status: 'pass', name: 'mt-link' } + ], + open_tracking_enabled: true, + click_tracking_enabled: false, + auto_unsubscribe_link_enabled: false, + custom_domain_tracking_enabled: false, + health_alerts_enabled: true, + critical_alerts_enabled: true, + alert_recipient_email: nil, + permissions: { can_read: true, can_update: true, can_destroy: true }, + created_at: nil, + updated_at: nil + ) + end + + context 'when sending domain does not exist' do + let(:sending_domain_id) { -1 } + + it 'raises not found error' do + expect { get }.to raise_error do |error| + expect(error).to be_a(Mailtrap::Error) + expect(error.message).to include('Not Found') + expect(error.messages.any? { |msg| msg.include?('Not Found') }).to be true + end + end + end + end + + describe '#create' do + subject(:create) { sending_domains_api.create(**request) } + + let(:request) do + { + domain_name: 'mailtrappio.com' + } + end + + it 'maps response data to sending domain object' do + expect(create).to be_a(Mailtrap::SendingDomain) + + expect(create).to match_struct( + id: 943_758, + domain_name: 'mailtrappio.com', + demo: false, + compliance_status: 'unverified_dns', + dns_verified: false, + dns_verified_at: '', + dns_records: + [ + { key: 'verification', domain: 'mt07.mailtrappio.com', type: 'CNAME', value: 'smtp.mailtrap.live', + status: 'missing', name: 'mt07' }, + { key: 'dkim1', domain: 'rwmt1._domainkey.mailtrappio.com', type: 'CNAME', + value: 'rwmt1.dkim.smtp.mailtrap.live', status: 'missing', name: 'rwmt1._domainkey' }, + { key: 'dkim2', domain: 'rwmt2._domainkey.mailtrappio.com', type: 'CNAME', + value: 'rwmt2.dkim.smtp.mailtrap.live', status: 'missing', name: 'rwmt2._domainkey' }, + { key: 'dmarc', name: '_dmarc' }, + { key: 'link_verification', domain: 'mt-link.mailtrappio.com', type: 'CNAME', value: 't.mailtrap.live', + status: 'missing', name: 'mt-link' } + ], + open_tracking_enabled: true, + click_tracking_enabled: false, + auto_unsubscribe_link_enabled: false, + custom_domain_tracking_enabled: false, + health_alerts_enabled: true, + critical_alerts_enabled: true, + alert_recipient_email: nil, + permissions: { can_read: true, can_update: true, can_destroy: true }, + created_at: nil, + updated_at: nil + ) + end + + context 'when API returns an error' do + let(:request) do + { + domain_name: '' # Invalid value, but present + } + end + + it 'raises a Mailtrap::Error' do + expect { create }.to raise_error do |error| + expect(error).to be_a(Mailtrap::Error) + expect(error.message).to include('client error') + end + end + end + end + + describe '#send_setup_instructions' do + subject(:send_setup_instructions) do + sending_domains_api.send_setup_instructions(domain_id: sending_domain_id, email: 'example@railsware.com') + end + + let(:sending_domain_id) { 943_758 } + + it 'returns nil' do + expect(send_setup_instructions).to be_nil + end + + context 'when sending domain does not exist' do + let(:sending_domain_id) { -1 } + + it 'raises not found error' do + expect { send_setup_instructions }.to raise_error do |error| + expect(error).to be_a(Mailtrap::Error) + expect(error.message).to include('Not Found') + expect(error.messages.any? { |msg| msg.include?('Not Found') }).to be true + end + end + end + end + + describe '#delete' do + subject(:delete) { sending_domains_api.delete(sending_domain_id) } + + let(:sending_domain_id) { 943_758 } + + it 'returns deleted sending domain data' do + expect(delete).to be_nil + end + + context 'when sending domain does not exist' do + let(:sending_domain_id) { -1 } + + it 'raises not found error' do + expect { delete }.to raise_error do |error| + expect(error).to be_a(Mailtrap::Error) + expect(error.message).to include('Not Found') + expect(error.messages.any? { |msg| msg.include?('Not Found') }).to be true + end + end + end + end +end