Skip to content

MT-23076: expose api token expiration in create and reset requests - #253

Open
oshchyhol wants to merge 6 commits into
mainfrom
MT-23076-dotnet-api-token-expiration
Open

MT-23076: expose api token expiration in create and reset requests#253
oshchyhol wants to merge 6 commits into
mainfrom
MT-23076-dotnet-api-token-expiration

Conversation

@oshchyhol

@oshchyhol oshchyhol commented Aug 7, 2026

Copy link
Copy Markdown

Motivation

MT-23076 – the Mailtrap API token endpoints now accept an optional expires_at value. This exposes it in the .NET SDK.

Changes

  • new ApiTokenExpiration type with a custom JSON converter implementing the tri-state contract: property unset – expires_at key omitted from the body (server default, a 1-year default is being rolled out behind a feature flag); ApiTokenExpiration.Never – explicit "expires_at": null (token never expires); ApiTokenExpiration.At(...) – ISO 8601 date-time string
  • CreateApiTokenRequest.ExpiresAt for the createApiToken operation (optional nullable expires_at in the request body)
  • new ResetApiTokenRequest and an IApiTokenResource.Reset(ResetApiTokenRequest, CancellationToken) overload for the resetApiToken operation, whose request body is now optional; the existing parameterless Reset() keeps sending no body
  • no client-side date validation – past, unparseable, or more-than-5-years-ahead values are rejected by the API with 422
  • Specifier.MaskedToken – the account-access ApiToken specifier has masked_token in the spec, which the SDK model was missing
  • unit and integration tests covering the three serialization cases, the unchanged no-body reset, and the 422 error path
  • api tokens example extended with expiration usage

Note: adding a member to IApiTokenResource is source-breaking for external implementors of that interface.

How to test

  • call client.Account(id).ApiTokens().Create(request) without setting ExpiresAt – the request body contains no expires_at key; with the server flag enabled the created token gets the default 1-year expires_at in the response
  • create with ExpiresAt = ApiTokenExpiration.Never – the request body contains "expires_at": null and the response has expires_at: null
  • create with ExpiresAt = ApiTokenExpiration.At(DateTimeOffset.Parse("2027-06-01T00:00:00Z")) – the request body contains the ISO date-time and the response echoes it
  • create with a past date – the call throws HttpRequestFailedException with status 422
  • call apiToken.Reset() (parameterless) – the request is sent without a body and succeeds exactly as before this change
  • call apiToken.Reset(new ResetApiTokenRequest { ExpiresAt = ApiTokenExpiration.Never }) – the request body is {"expires_at":null} and the new token never expires
  • list tokens and get token details – expires_at is returned in responses as before

Companion PRs

Caveat: release/merge only after falcon deploys MT-23076 and zap_api_token_expiration is enabled in production.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6ac2679f-fc66-40f1-ae4b-8ef738b87417


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Exposes API-token expiration controls across creation and reset operations.

Changes:

  • Adds tri-state expiration serialization.
  • Adds expiration-aware reset support and tests.
  • Adds masked-token account-access data.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/Mailtrap.UnitTests/GlobalUsings.cs Imports expiration models.
tests/Mailtrap.UnitTests/ApiTokens/ApiTokenResourceTests.cs Tests null reset requests.
tests/Mailtrap.UnitTests/ApiTokens/ApiTokenExpirationTests.cs Tests expiration serialization.
tests/Mailtrap.IntegrationTests/ApiTokens/Reset_WithNeverExpiration_Success.json Never-expiring reset fixture.
tests/Mailtrap.IntegrationTests/ApiTokens/Reset_WithExpiration_Success.json Expiring reset fixture.
tests/Mailtrap.IntegrationTests/ApiTokens/Create_WithNeverExpiration_Success.json Never-expiring creation fixture.
tests/Mailtrap.IntegrationTests/ApiTokens/Create_WithExpiration_Success.json Expiring creation fixture.
tests/Mailtrap.IntegrationTests/ApiTokens/Create_ShouldThrow_WhenExpirationIsRejected.json Rejected-expiration fixture.
tests/Mailtrap.IntegrationTests/ApiTokens/ApiTokensIntegrationTests.cs Tests request bodies and responses.
src/Mailtrap/ApiTokens/ApiTokenResource.cs Implements reset with request body.
src/Mailtrap.Abstractions/GlobalUsings.cs Imports expiration converter.
src/Mailtrap.Abstractions/ApiTokens/Requests/ResetApiTokenRequest.cs Defines reset request model.
src/Mailtrap.Abstractions/ApiTokens/Requests/CreateApiTokenRequest.cs Adds creation expiration.
src/Mailtrap.Abstractions/ApiTokens/Models/ApiTokenExpiration.cs Defines expiration value type.
src/Mailtrap.Abstractions/ApiTokens/IApiTokenResource.cs Exposes reset overload.
src/Mailtrap.Abstractions/ApiTokens/Converters/ApiTokenExpirationJsonConverter.cs Implements expiration JSON conversion.
src/Mailtrap.Abstractions/AccountAccesses/Models/Specifier.cs Adds masked-token field.
examples/Mailtrap.Example.ApiTokens/Program.cs Demonstrates expiration usage.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +26 to +39
public override void Write(Utf8JsonWriter writer, ApiTokenExpiration value, JsonSerializerOptions options)
{
Ensure.NotNull(writer, nameof(writer));
Ensure.NotNull(value, nameof(value));

if (value.Value.HasValue)
{
writer.WriteStringValue(value.Value.Value);
}
else
{
writer.WriteNullValue();
}
}
Comment on lines +93 to +95
[JsonPropertyName("masked_token")]
[JsonPropertyOrder(6)]
public string? MaskedToken { get; set; }
/// The old token stops working after a short grace period. The response includes
/// the new token value – store it securely; it is only returned once.
/// </remarks>
public Task<ApiTokenResetResponse> Reset(ResetApiTokenRequest request, CancellationToken cancellationToken = default);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants