diff --git a/bin/record-vcr b/bin/record-vcr new file mode 100755 index 0000000..78136b1 --- /dev/null +++ b/bin/record-vcr @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Run RSpec against the live Mailtrap API to record (or re-record) VCR cassettes. +# Real HTTP calls are made — only run this when you intend to update the +# cassettes under spec/fixtures/vcr_cassettes/. Credentials are injected from +# 1Password at runtime. +# +# Usage: +# bin/record-vcr # all specs +# bin/record-vcr spec/mailtrap/sending_domains_api_spec.rb # single file +# bin/record-vcr spec/mailtrap/sending_domains_api_spec.rb:42 # single example +# bin/record-vcr -e "#update" # filter by name +# bin/record-vcr --org spec/mailtrap/sub_accounts_api_spec.rb # use organization API token +# +# Pass --org as the first argument to authenticate with the organization-level token +# (needed for organization-scoped endpoints like SubAccountsAPI). Default is the +# account-level token. +# +# Requires the 1Password CLI (`op`) to be installed and signed in. + +set -euo pipefail + +export MAILTRAP_ACCOUNT_ID="op://Mailtrap Dev/Mailtrap SDK Dev API Key/account_id" +export MAILTRAP_ORGANIZATION_ID="op://Mailtrap Dev/Mailtrap SDK Dev API Key/organization_id" +export MAILTRAP_API_KEY="op://Mailtrap Dev/Mailtrap SDK Dev API Key/account_api_token" +export MAILTRAP_ORGANIZATION_API_KEY="op://Mailtrap Dev/Mailtrap SDK Dev API Key/organization_api_token" + +exec op run -- bundle exec rspec "$@" diff --git a/examples/api_tokens_api.rb b/examples/api_tokens_api.rb new file mode 100644 index 0000000..7aec743 --- /dev/null +++ b/examples/api_tokens_api.rb @@ -0,0 +1,9 @@ +require 'mailtrap' + +account_id = 3229 +client = Mailtrap::Client.new(api_key: 'your-api-key') +api_tokens = Mailtrap::ApiTokensAPI.new(account_id, client) + +# List API tokens +api_tokens.list +# => [#, ...] diff --git a/examples/permissions_api.rb b/examples/permissions_api.rb new file mode 100644 index 0000000..4133453 --- /dev/null +++ b/examples/permissions_api.rb @@ -0,0 +1,15 @@ +require 'mailtrap' + +account_id = 3229 +client = Mailtrap::Client.new(api_key: 'your-api-key') +permissions = Mailtrap::PermissionsAPI.new(account_id, client) + +# Bulk-update user/token permissions on an account access. Combine create/update with destroy: +permissions.bulk_update( + 5142, # account_access_id + [ + { resource_id: '3281', resource_type: 'account', access_level: 'viewer' }, + { resource_id: '3809', resource_type: 'inbox', _destroy: true } + ] +) +# => { message: "Permissions have been updated!" } diff --git a/examples/sub_accounts_api.rb b/examples/sub_accounts_api.rb new file mode 100644 index 0000000..f24790f --- /dev/null +++ b/examples/sub_accounts_api.rb @@ -0,0 +1,13 @@ +require 'mailtrap' + +organization_id = 4567 +client = Mailtrap::Client.new(api_key: 'your-api-key') +sub_accounts = Mailtrap::SubAccountsAPI.new(organization_id, client) + +# List sub accounts under the organization +sub_accounts.list +# => [#, ...] + +# Create a new sub account +sub_accounts.create(name: 'New Team Account') +# => # diff --git a/lib/mailtrap.rb b/lib/mailtrap.rb index d94e809..f5e52a8 100644 --- a/lib/mailtrap.rb +++ b/lib/mailtrap.rb @@ -6,6 +6,9 @@ require_relative 'mailtrap/version' require_relative 'mailtrap/accounts_api' require_relative 'mailtrap/account_accesses_api' +require_relative 'mailtrap/api_tokens_api' +require_relative 'mailtrap/permissions_api' +require_relative 'mailtrap/sub_accounts_api' require_relative 'mailtrap/billing_api' require_relative 'mailtrap/email_templates_api' require_relative 'mailtrap/contacts_api' diff --git a/lib/mailtrap/api_token.rb b/lib/mailtrap/api_token.rb new file mode 100644 index 0000000..11da232 --- /dev/null +++ b/lib/mailtrap/api_token.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Mailtrap + # Data Transfer Object for API Token + # @attr_reader id [Integer] The API token ID + # @attr_reader name [String] Token display name + # @attr_reader last_4_digits [String] Last 4 characters of the token + # @attr_reader created_by [String] Name of the user or token that created this token + # @attr_reader expires_at [String, nil] When the token expires (ISO 8601); nil if it does not expire + # @attr_reader resources [Array] Permissions granted to this token + # @attr_reader token [String, nil] Full token value — only populated by #create and #reset + ApiToken = Struct.new( + :id, + :name, + :last_4_digits, + :created_by, + :expires_at, + :resources, + :token, + keyword_init: true + ) +end diff --git a/lib/mailtrap/api_tokens_api.rb b/lib/mailtrap/api_tokens_api.rb new file mode 100644 index 0000000..4333238 --- /dev/null +++ b/lib/mailtrap/api_tokens_api.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require_relative 'base_api' +require_relative 'api_token' + +module Mailtrap + class ApiTokensAPI + include BaseAPI + + self.response_class = ApiToken + + # Lists API tokens visible to the current API token + # @return [Array] Array of API tokens + # @!macro api_errors + def list + base_list + end + + private + + def base_path + "/api/accounts/#{account_id}/api_tokens" + end + end +end diff --git a/lib/mailtrap/client.rb b/lib/mailtrap/client.rb index 321db1d..db2af43 100644 --- a/lib/mailtrap/client.rb +++ b/lib/mailtrap/client.rb @@ -207,6 +207,20 @@ def patch(path, body = nil) ) end + # Performs a PUT request to the specified path + # @param path [String] The request path + # @param body [Hash] The request body + # @return [Hash, String, nil] JSON response or raw response body + # @!macro api_errors + def put(path, body = nil) + perform_request( + method: :put, + host: general_api_host, + path:, + body: + ) + end + # Performs a DELETE request to the specified path # @param path [String] The request path # @return [Hash, String, nil] JSON response or raw response body @@ -261,6 +275,8 @@ def setup_request(method, uri_or_path, body = nil) Net::HTTP::Post.new(uri_or_path) when :patch Net::HTTP::Patch.new(uri_or_path) + when :put + Net::HTTP::Put.new(uri_or_path) when :delete Net::HTTP::Delete.new(uri_or_path) else diff --git a/lib/mailtrap/permissions_api.rb b/lib/mailtrap/permissions_api.rb new file mode 100644 index 0000000..5fa7b78 --- /dev/null +++ b/lib/mailtrap/permissions_api.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require_relative 'base_api' + +module Mailtrap + class PermissionsAPI + include BaseAPI + + # Bulk-updates user or token permissions on an account access + # @param account_access_id [Integer] The account access ID + # @param permissions [Array] Array of permission entries + # - `{ resource_id:, resource_type:, access_level: }` to create or update + # - `{ resource_id:, resource_type:, _destroy: true }` to remove + # @return [Hash] API response (e.g. `{ message: 'Permissions have been updated!' }`) + # @!macro api_errors + def bulk_update(account_access_id, permissions) + client.put( + "#{base_path}/account_accesses/#{account_access_id}/permissions/bulk", + { permissions: permissions } + ) + end + + private + + def base_path + "/api/accounts/#{account_id}" + end + end +end diff --git a/lib/mailtrap/sub_account.rb b/lib/mailtrap/sub_account.rb new file mode 100644 index 0000000..47a8baf --- /dev/null +++ b/lib/mailtrap/sub_account.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Mailtrap + # Data Transfer Object for an organization sub account + # @attr_reader id [Integer] The sub account ID + # @attr_reader name [String] The sub account name + SubAccount = Struct.new( + :id, + :name, + keyword_init: true + ) +end diff --git a/lib/mailtrap/sub_accounts_api.rb b/lib/mailtrap/sub_accounts_api.rb new file mode 100644 index 0000000..e71ceec --- /dev/null +++ b/lib/mailtrap/sub_accounts_api.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require_relative 'base_api' +require_relative 'sub_account' + +module Mailtrap + class SubAccountsAPI + include BaseAPI + + attr_reader :organization_id + + self.supported_options = %i[name].freeze + + self.response_class = SubAccount + + # @param organization_id [Integer] The organization ID + # @param client [Mailtrap::Client] The client instance + # @raise [ArgumentError] If organization_id is nil + def initialize(organization_id, client = Mailtrap::Client.new) + raise ArgumentError, 'organization_id is required' if organization_id.nil? + + @organization_id = organization_id + @client = client + end + + # Lists all sub accounts under the organization + # @return [Array] Array of sub accounts + # @!macro api_errors + def list + base_list + end + + # Creates a new sub account under the organization + # @param [Hash] options The parameters to create + # @option options [String] :name Name of the sub account + # @return [SubAccount] Created sub account + # @!macro api_errors + # @raise [ArgumentError] If invalid options are provided + def create(options) + base_create(options) + end + + private + + def base_path + "/api/organizations/#{organization_id}/sub_accounts" + end + + def wrap_request(options) + { account: options } + end + end +end diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_list/maps_response_data_to_ApiToken_objects.yml b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_list/maps_response_data_to_ApiToken_objects.yml new file mode 100644 index 0000000..8b9e29e --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_list/maps_response_data_to_ApiToken_objects.yml @@ -0,0 +1,74 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/accounts/1111111/api_tokens + 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: + - Tue, 28 Apr 2026 14:05:37 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/"7e2dfc9492802e3dbd170f4b6324efb3" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.038392' + Strict-Transport-Security: + - max-age=2592000; includeSubDomains; preload + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '[{"resources":[{"resource_type":"account","resource_id":1719941,"access_level":100}],"id":2496915,"name":"Ruby SDK Dev","last_4_digits":"9daa","created_by":"John Doe","expires_at":null}]' + recorded_at: Tue, 28 Apr 2026 14:05:37 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_PermissionsAPI/_bulk_update/returns_the_API_success_message.yml b/spec/fixtures/vcr_cassettes/Mailtrap_PermissionsAPI/_bulk_update/returns_the_API_success_message.yml new file mode 100644 index 0000000..64a1da4 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_PermissionsAPI/_bulk_update/returns_the_API_success_message.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: put + uri: https://mailtrap.io/api/accounts/1111111/account_accesses/5339621/permissions/bulk + body: + encoding: UTF-8 + string: '{"permissions":[{"resource_id":"1719941","resource_type":"account","access_level":"viewer"}]}' + 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: + - Tue, 28 Apr 2026 14:43:16 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '44' + 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' + Etag: + - W/"a6f38e5f5257d228ea419b1576fa9aa4" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.066854' + Strict-Transport-Security: + - max-age=2592000; includeSubDomains; preload + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"message":"Permissions have been updated!"}' + recorded_at: Tue, 28 Apr 2026 14:43:16 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_PermissionsAPI/_bulk_update/when_account_access_does_not_exist/raises_not_found_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_PermissionsAPI/_bulk_update/when_account_access_does_not_exist/raises_not_found_error.yml new file mode 100644 index 0000000..ee772ef --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_PermissionsAPI/_bulk_update/when_account_access_does_not_exist/raises_not_found_error.yml @@ -0,0 +1,71 @@ +--- +http_interactions: +- request: + method: put + uri: https://mailtrap.io/api/accounts/1111111/account_accesses/-1/permissions/bulk + body: + encoding: UTF-8 + string: '{"permissions":[{"resource_id":"1719941","resource_type":"account","access_level":"viewer"}]}' + 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: + - Tue, 28 Apr 2026 14:45:02 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '21' + 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-Runtime: + - '0.021482' + Strict-Transport-Security: + - max-age=2592000; includeSubDomains; preload + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"error":"Not Found"}' + recorded_at: Tue, 28 Apr 2026 14:45:02 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SubAccountsAPI/_create/maps_response_data_to_SubAccount_object.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SubAccountsAPI/_create/maps_response_data_to_SubAccount_object.yml new file mode 100644 index 0000000..4b03f13 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SubAccountsAPI/_create/maps_response_data_to_SubAccount_object.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/organizations/2222222/sub_accounts + body: + encoding: UTF-8 + string: '{"account":{"name":"SDK Test Sub Account"}}' + 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: + - Wed, 29 Apr 2026 11:44:41 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '44' + 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' + Etag: + - W/"0b27462f8e3ad4f086b8334c71808f6a" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.853483' + Strict-Transport-Security: + - max-age=2592000; includeSubDomains; preload + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"id":2704237,"name":"SDK Test Sub Account"}' + recorded_at: Wed, 29 Apr 2026 11:44:41 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SubAccountsAPI/_create/when_name_is_missing/raises_a_Mailtrap_Error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SubAccountsAPI/_create/when_name_is_missing/raises_a_Mailtrap_Error.yml new file mode 100644 index 0000000..8235a7d --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SubAccountsAPI/_create/when_name_is_missing/raises_a_Mailtrap_Error.yml @@ -0,0 +1,49 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/organizations/2222222/sub_accounts + body: + encoding: UTF-8 + string: '{"account":{}}' + 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: 400 + message: Bad Request + headers: + Date: + - Wed, 29 Apr 2026 11:44:42 GMT + Content-Type: + - text/html; charset=UTF-8 + Content-Length: + - '0' + Connection: + - keep-alive + Server: + - cloudflare + X-Runtime: + - '0.016264' + Strict-Transport-Security: + - max-age=2592000; includeSubDomains; preload + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '' + recorded_at: Wed, 29 Apr 2026 11:44:42 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_SubAccountsAPI/_list/maps_response_data_to_SubAccount_objects.yml b/spec/fixtures/vcr_cassettes/Mailtrap_SubAccountsAPI/_list/maps_response_data_to_SubAccount_objects.yml new file mode 100644 index 0000000..3fbd787 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_SubAccountsAPI/_list/maps_response_data_to_SubAccount_objects.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/organizations/2222222/sub_accounts + 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: + - Wed, 29 Apr 2026 12:29: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: + - '149' + Etag: + - W/"c47bbdaaa9593d14ae0a77e4cb385c2a" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.012700' + Strict-Transport-Security: + - max-age=2592000; includeSubDomains; preload + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '[{"id":2704237,"name":"SDK Test Sub Account"}]' + recorded_at: Wed, 29 Apr 2026 12:29:02 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/mailtrap/api_token_spec.rb b/spec/mailtrap/api_token_spec.rb new file mode 100644 index 0000000..5d06582 --- /dev/null +++ b/spec/mailtrap/api_token_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::ApiToken do + describe '#initialize' do + subject(:api_token) { described_class.new(attributes) } + + let(:attributes) do + { + id: 12_345, + name: 'My API Token', + last_4_digits: 'x7k9', + created_by: 'user@example.com', + expires_at: nil, + resources: [{ resource_type: 'account', resource_id: 3229, access_level: 100 }], + token: 'a1b2c3d4e5f6g7h8' + } + end + + it 'creates an api token with all attributes' do + expect(api_token).to have_attributes(attributes) + end + end +end diff --git a/spec/mailtrap/api_tokens_api_spec.rb b/spec/mailtrap/api_tokens_api_spec.rb new file mode 100644 index 0000000..43a66eb --- /dev/null +++ b/spec/mailtrap/api_tokens_api_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::ApiTokensAPI, :vcr do + subject(:api_tokens_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) { api_tokens_api.list } + + it 'maps response data to ApiToken objects' do + expect(list).to all(be_a(Mailtrap::ApiToken)) + expect(list.first).to have_attributes( + id: an_instance_of(Integer), + name: an_instance_of(String), + token: nil + ) + end + end +end diff --git a/spec/mailtrap/permissions_api_spec.rb b/spec/mailtrap/permissions_api_spec.rb new file mode 100644 index 0000000..be58083 --- /dev/null +++ b/spec/mailtrap/permissions_api_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::PermissionsAPI, :vcr do + subject(:permissions_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 '#bulk_update' do + subject(:bulk_update) { permissions_api.bulk_update(account_access_id, permissions) } + + let(:account_access_id) { 5_339_621 } + let(:permissions) do + [ + { resource_id: '1719941', resource_type: 'account', access_level: 'viewer' } + ] + end + + it 'returns the API success message' do + expect(bulk_update).to include(message: an_instance_of(String)) + end + + context 'when account access does not exist' do + let(:account_access_id) { -1 } + + it 'raises not found error' do + expect { bulk_update }.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 diff --git a/spec/mailtrap/sub_account_spec.rb b/spec/mailtrap/sub_account_spec.rb new file mode 100644 index 0000000..c1054f1 --- /dev/null +++ b/spec/mailtrap/sub_account_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::SubAccount do + describe '#initialize' do + subject(:sub_account) { described_class.new(attributes) } + + let(:attributes) { { id: 12_345, name: 'Development Team Account' } } + + it 'creates a sub account with all attributes' do + expect(sub_account).to have_attributes(attributes) + end + end +end diff --git a/spec/mailtrap/sub_accounts_api_spec.rb b/spec/mailtrap/sub_accounts_api_spec.rb new file mode 100644 index 0000000..e364d50 --- /dev/null +++ b/spec/mailtrap/sub_accounts_api_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::SubAccountsAPI, :vcr do + subject(:sub_accounts_api) { described_class.new(organization_id, client) } + + let(:organization_id) { ENV.fetch('MAILTRAP_ORGANIZATION_ID', 2_222_222) } + let(:client) { Mailtrap::Client.new(api_key: ENV.fetch('MAILTRAP_ORGANIZATION_API_KEY', 'local-org-api-key')) } + + describe '#initialize' do + it 'raises ArgumentError when organization_id is nil' do + expect { described_class.new(nil, client) } + .to raise_error(ArgumentError, 'organization_id is required') + end + end + + describe '#list' do + subject(:list) { sub_accounts_api.list } + + it 'maps response data to SubAccount objects' do + expect(list).to all(be_a(Mailtrap::SubAccount)) + expect(list.first).to have_attributes( + id: an_instance_of(Integer), + name: an_instance_of(String) + ) + end + end + + describe '#create' do + subject(:create) { sub_accounts_api.create(request) } + + let(:request) { { name: 'SDK Test Sub Account' } } + + it 'maps response data to SubAccount object' do + expect(create).to be_a(Mailtrap::SubAccount) + expect(create).to have_attributes( + id: an_instance_of(Integer), + name: 'SDK Test Sub Account' + ) + end + + context 'when invalid options are provided' do + let(:request) { { unknown_option: true } } + + it 'raises ArgumentError' do + expect { create }.to raise_error(ArgumentError, /invalid options are given/) + end + end + + context 'when name is missing' do + let(:request) { {} } + + it 'raises a Mailtrap::Error' do + expect { create }.to raise_error(Mailtrap::Error) + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ef1be10..678327d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -22,7 +22,9 @@ next if interaction.request.uri =~ /localhost/ interaction.request.uri.gsub!(%r{/accounts/\d+/}, '/accounts/1111111/') + interaction.request.uri.gsub!(%r{/organizations/\d+/}, '/organizations/2222222/') interaction.response.body.gsub!(/"account_id":\d+/, '"account_id": 1111111') + interaction.response.body.gsub!(/"organization_id":\d+/, '"account_id": 2222222') interaction.response.body.gsub!(/"username":"[^"]*"/, '"username": "railsware"') interaction.response.body.gsub!(/"password":"[^"]*"/, '"password": "xxxxxxxx"') interaction.response.body.gsub!(/"email":"[^"]*"/, '"email": "welcome@rw.com"')