diff --git a/examples/api_tokens_api.rb b/examples/api_tokens_api.rb index 7aec743..8954fdd 100644 --- a/examples/api_tokens_api.rb +++ b/examples/api_tokens_api.rb @@ -7,3 +7,24 @@ # List API tokens api_tokens.list # => [#, ...] + +# Get a single API token (the `token` field is nil — the full value is only returned by create/reset) +api_tokens.get(12_345) +# => # + +# Create a new API token. The full `token` value is returned ONLY once — store it securely. +api_tokens.create( + name: 'My API Token', + resources: [ + { resource_type: 'account', resource_id: account_id, access_level: 100 } + ] +) +# => # + +# Reset a token — expires the old value (short grace period) and returns a new value once. +api_tokens.reset(12_345) +# => # + +# Permanently delete a token +api_tokens.delete(12_345) +# => nil diff --git a/examples/permissions_api.rb b/examples/permissions_api.rb index 4133453..b108f51 100644 --- a/examples/permissions_api.rb +++ b/examples/permissions_api.rb @@ -4,6 +4,16 @@ client = Mailtrap::Client.new(api_key: 'your-api-key') permissions = Mailtrap::PermissionsAPI.new(account_id, client) +# Get the recursive tree of resources the current token can access +permissions.resources +# => [ +# # +# ]> +# ] + # Bulk-update user/token permissions on an account access. Combine create/update with destroy: permissions.bulk_update( 5142, # account_access_id diff --git a/lib/mailtrap/api_tokens_api.rb b/lib/mailtrap/api_tokens_api.rb index 4333238..5249bb8 100644 --- a/lib/mailtrap/api_tokens_api.rb +++ b/lib/mailtrap/api_tokens_api.rb @@ -7,6 +7,8 @@ module Mailtrap class ApiTokensAPI include BaseAPI + self.supported_options = %i[name resources].freeze + self.response_class = ApiToken # Lists API tokens visible to the current API token @@ -16,6 +18,46 @@ def list base_list end + # Retrieves a single API token by ID + # @param token_id [Integer] The API token ID + # @return [ApiToken] API token object (the `token` field is nil — full value is only + # returned by #create and #reset) + # @!macro api_errors + def get(token_id) + base_get(token_id) + end + + # Creates a new API token. The full `token` value is returned ONLY ONCE — store it securely. + # @param [Hash] options The parameters to create + # @option options [String] :name Display name for the token + # @option options [Array] :resources Permissions to assign + # - `{ resource_type:, resource_id:, access_level: }` + # @return [ApiToken] Created token (full `token` value populated) + # @!macro api_errors + # @raise [ArgumentError] If invalid options are provided + def create(options) + base_create(options) + end + + # Expires the requested token and returns a new one with the same permissions. + # The old token stops working after a short grace period. The new `token` value is + # returned ONLY ONCE — store it securely + # @param token_id [Integer] The API token ID + # @return [ApiToken] New token (full `token` value populated) + # @!macro api_errors + def reset(token_id) + response = client.post("#{base_path}/#{token_id}/reset") + handle_response(response) + end + + # Permanently deletes an API token + # @param token_id [Integer] The API token ID + # @return nil + # @!macro api_errors + def delete(token_id) + base_delete(token_id) + end + private def base_path diff --git a/lib/mailtrap/permission_resource.rb b/lib/mailtrap/permission_resource.rb new file mode 100644 index 0000000..044bd3c --- /dev/null +++ b/lib/mailtrap/permission_resource.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Mailtrap + # Data Transfer Object for a node in the Permissions resources tree + # @attr_reader id [Integer] Resource ID + # @attr_reader name [String] Resource name + # @attr_reader type [String] Resource type (account, project, inbox, sending_domain, ...) + # @attr_reader access_level [Integer] The access level the current token has on this resource + # @attr_reader resources [Array] Nested child resources + PermissionResource = Struct.new( + :id, + :name, + :type, + :access_level, + :resources, + keyword_init: true + ) +end diff --git a/lib/mailtrap/permissions_api.rb b/lib/mailtrap/permissions_api.rb index 5fa7b78..5fa5d41 100644 --- a/lib/mailtrap/permissions_api.rb +++ b/lib/mailtrap/permissions_api.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative 'base_api' +require_relative 'permission_resource' module Mailtrap class PermissionsAPI @@ -20,10 +21,31 @@ def bulk_update(account_access_id, permissions) ) end + # Returns the recursive tree of resources the current token can access. + # Each node carries the token's access_level and any nested child resources. + # @return [Array] Top-level resources, each with nested children + # @!macro api_errors + def resources + response = client.get("#{base_path}/permissions/resources") + build_resource_tree(response) + end + private def base_path "/api/accounts/#{account_id}" end + + def build_resource_tree(items) + items.map do |item| + PermissionResource.new( + id: item[:id], + name: item[:name], + type: item[:type], + access_level: item[:access_level], + resources: build_resource_tree(Array(item[:resources])) + ) + end + end end end diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_create/maps_response_data_to_ApiToken_with_full_token_value.yml b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_create/maps_response_data_to_ApiToken_with_full_token_value.yml new file mode 100644 index 0000000..4d3ed89 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_create/maps_response_data_to_ApiToken_with_full_token_value.yml @@ -0,0 +1,74 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/accounts/1111111/api_tokens + body: + encoding: UTF-8 + string: '{"name":"Ruby SDK Test Token","resources":[{"resource_type":"account","resource_id":1111111,"access_level":100}]}' + 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 14:21:57 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/"bcc82ee33e2e4e0ca856e839d077c5cb" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.138791' + 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":2498713,"name":"Ruby + SDK Test Token","last_4_digits":"dcf7","created_by":"SDK Dev Account Token","expires_at":null,"token":"d5335b07610bd99b7fbc03f80e26dcf7"}' + recorded_at: Wed, 29 Apr 2026 14:21:57 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_create/when_name_is_missing/raises_a_Mailtrap_Error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_create/when_name_is_missing/raises_a_Mailtrap_Error.yml new file mode 100644 index 0000000..c1886bb --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_create/when_name_is_missing/raises_a_Mailtrap_Error.yml @@ -0,0 +1,72 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/accounts/1111111/api_tokens + body: + encoding: UTF-8 + string: '{"resources":[]}' + 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: + - Wed, 29 Apr 2026 14:21:57 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '76' + 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-Runtime: + - '0.010865' + Strict-Transport-Security: + - max-age=2592000; includeSubDomains; preload + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"errors":{"base":["No permissions selected. Please choose at least + one."]}}' + recorded_at: Wed, 29 Apr 2026 14:21:57 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_delete/returns_nil_on_success.yml b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_delete/returns_nil_on_success.yml new file mode 100644 index 0000000..42d1b22 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_delete/returns_nil_on_success.yml @@ -0,0 +1,65 @@ +--- +http_interactions: +- request: + method: delete + uri: https://mailtrap.io/api/accounts/1111111/api_tokens/2498713 + 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: + - Wed, 29 Apr 2026 14:27:58 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-Runtime: + - '0.089181' + 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 14:27:58 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_delete/when_token_does_not_exist/raises_not_found_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_delete/when_token_does_not_exist/raises_not_found_error.yml new file mode 100644 index 0000000..96a52a6 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_delete/when_token_does_not_exist/raises_not_found_error.yml @@ -0,0 +1,71 @@ +--- +http_interactions: +- request: + method: delete + uri: https://mailtrap.io/api/accounts/1111111/api_tokens/-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: + - Wed, 29 Apr 2026 14:27:59 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: + - '148' + Cache-Control: + - no-cache + X-Runtime: + - '0.021454' + 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: Wed, 29 Apr 2026 14:27:59 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_get/maps_response_data_to_ApiToken_object.yml b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_get/maps_response_data_to_ApiToken_object.yml new file mode 100644 index 0000000..c1b4729 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_get/maps_response_data_to_ApiToken_object.yml @@ -0,0 +1,74 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/accounts/1111111/api_tokens/2498561 + 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 14:16:50 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/"26fcf7c940c3e03455c6814d8f4822c2" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.022932' + 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":2498561,"name":"SDK + Dev Account Token","last_4_digits":"f44e","created_by":"John Doe","expires_at":null}' + recorded_at: Wed, 29 Apr 2026 14:16:50 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_get/when_token_does_not_exist/raises_not_found_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_get/when_token_does_not_exist/raises_not_found_error.yml new file mode 100644 index 0000000..8805d8f --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_get/when_token_does_not_exist/raises_not_found_error.yml @@ -0,0 +1,71 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/accounts/1111111/api_tokens/-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: + - Wed, 29 Apr 2026 14:17:43 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.013935' + 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: Wed, 29 Apr 2026 14:17:43 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_reset/maps_response_data_to_ApiToken_with_new_token_value.yml b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_reset/maps_response_data_to_ApiToken_with_new_token_value.yml new file mode 100644 index 0000000..be79473 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_reset/maps_response_data_to_ApiToken_with_new_token_value.yml @@ -0,0 +1,75 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/accounts/1111111/api_tokens/2498713/reset + body: + encoding: UTF-8 + 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 14:25:08 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/"8c6c55f98ae457d13e54547c3b370ba0" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.164567' + 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":2498715,"name":"Ruby + SDK Test Token 2498715","last_4_digits":"856f","created_by":"SDK Dev Account + Token","expires_at":null,"token":"ff828c557cb54180a0ce279fd6d5856f"}' + recorded_at: Wed, 29 Apr 2026 14:25:08 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_reset/when_token_does_not_exist/raises_not_found_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_reset/when_token_does_not_exist/raises_not_found_error.yml new file mode 100644 index 0000000..1bdd863 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_ApiTokensAPI/_reset/when_token_does_not_exist/raises_not_found_error.yml @@ -0,0 +1,71 @@ +--- +http_interactions: +- request: + method: post + uri: https://mailtrap.io/api/accounts/1111111/api_tokens/-1/reset + body: + encoding: UTF-8 + 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: + - Wed, 29 Apr 2026 14:25:09 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: + - '148' + Cache-Control: + - no-cache + X-Runtime: + - '0.030703' + 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: Wed, 29 Apr 2026 14:25:09 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_PermissionsAPI/_resources/returns_a_tree_of_PermissionResource_objects.yml b/spec/fixtures/vcr_cassettes/Mailtrap_PermissionsAPI/_resources/returns_a_tree_of_PermissionResource_objects.yml new file mode 100644 index 0000000..4d9ca8e --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_PermissionsAPI/_resources/returns_a_tree_of_PermissionResource_objects.yml @@ -0,0 +1,74 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/accounts/1111111/permissions/resources + 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 14:31:57 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/"f954e5f16efdf2fddb0e6d7abadd3543" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.024584' + Strict-Transport-Security: + - max-age=2592000; includeSubDomains; preload + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '[{"id": 1719941,"name": "Account","type": "account","access_level": 100,"resources": []}]' + recorded_at: Wed, 29 Apr 2026 14:31:57 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/mailtrap/api_tokens_api_spec.rb b/spec/mailtrap/api_tokens_api_spec.rb index 43a66eb..3b494c2 100644 --- a/spec/mailtrap/api_tokens_api_spec.rb +++ b/spec/mailtrap/api_tokens_api_spec.rb @@ -18,4 +18,117 @@ ) end end + + describe '#get' do + subject(:get) { api_tokens_api.get(token_id) } + + let(:token_id) { 2_498_561 } + + it 'maps response data to ApiToken object' do + expect(get).to be_a(Mailtrap::ApiToken) + expect(get).to have_attributes( + id: token_id, + name: an_instance_of(String), + token: nil + ) + end + + context 'when token does not exist' do + let(:token_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) { api_tokens_api.create(request) } + + let(:request) do + { + name: 'Ruby SDK Test Token', + resources: [ + { resource_type: 'account', resource_id: account_id, access_level: 100 } + ] + } + end + + it 'maps response data to ApiToken with full token value' do + expect(create).to be_a(Mailtrap::ApiToken) + expect(create).to have_attributes( + id: an_instance_of(Integer), + name: 'Ruby SDK Test Token', + token: an_instance_of(String) + ) + 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) { { resources: [] } } + + it 'raises a Mailtrap::Error' do + expect { create }.to raise_error(Mailtrap::Error) + end + end + end + + describe '#reset' do + subject(:reset) { api_tokens_api.reset(token_id) } + + let(:token_id) { 2_498_713 } + + it 'maps response data to ApiToken with new token value' do + expect(reset).to be_a(Mailtrap::ApiToken) + expect(reset).to have_attributes( + id: an_instance_of(Integer), + token: an_instance_of(String) + ) + end + + context 'when token does not exist' do + let(:token_id) { -1 } + + it 'raises not found error' do + expect { reset }.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) { api_tokens_api.delete(token_id) } + + let(:token_id) { 2_498_713 } + + it 'returns nil on success' do + expect(delete).to be_nil + end + + context 'when token does not exist' do + let(:token_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 diff --git a/spec/mailtrap/permission_resource_spec.rb b/spec/mailtrap/permission_resource_spec.rb new file mode 100644 index 0000000..401f592 --- /dev/null +++ b/spec/mailtrap/permission_resource_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::PermissionResource do + describe '#initialize' do + subject(:resource) { described_class.new(attributes) } + + let(:child) do + described_class.new( + id: 3816, + name: 'My First Inbox', + type: 'inbox', + access_level: 100, + resources: [] + ) + end + + let(:attributes) do + { + id: 4001, + name: 'My First Project', + type: 'project', + access_level: 1, + resources: [child] + } + end + + it 'creates a permission resource with all attributes and nested children' do + expect(resource).to have_attributes( + id: 4001, + name: 'My First Project', + type: 'project', + access_level: 1 + ) + expect(resource.resources).to eq([child]) + end + end +end diff --git a/spec/mailtrap/permissions_api_spec.rb b/spec/mailtrap/permissions_api_spec.rb index 809d99f..e433ad1 100644 --- a/spec/mailtrap/permissions_api_spec.rb +++ b/spec/mailtrap/permissions_api_spec.rb @@ -9,7 +9,7 @@ describe '#bulk_update' do subject(:bulk_update) { permissions_api.bulk_update(account_access_id, permissions) } - let(:account_access_id) { 5339621 } + let(:account_access_id) { 5_339_621 } let(:permissions) do [ { resource_id: '1719941', resource_type: 'account', access_level: 'viewer' } @@ -32,4 +32,19 @@ end end end + + describe '#resources' do + subject(:resources) { permissions_api.resources } + + it 'returns a tree of PermissionResource objects' do + expect(resources).to all(be_a(Mailtrap::PermissionResource)) + expect(resources.first).to have_attributes( + id: an_instance_of(Integer), + name: an_instance_of(String), + type: an_instance_of(String), + access_level: an_instance_of(Integer) + ) + expect(resources.first.resources).to all(be_a(Mailtrap::PermissionResource)) + end + end end