diff --git a/.env.example b/.env.example
index 7f3f684..497b5d7 100644
--- a/.env.example
+++ b/.env.example
@@ -4,6 +4,8 @@ MAILINATOR_TEST_API_TOKEN=
# Optional values used by endpoint-specific integration tests.
MAILINATOR_TEST_DOMAIN_PRIVATE=
MAILINATOR_TEST_INBOX=
+# Existing email in MAILINATOR_TEST_DOMAIN_PRIVATE, used by header and summary retrieval tests.
+MAILINATOR_TEST_MESSAGE_ID=
MAILINATOR_TEST_PHONE_NUMBER=
MAILINATOR_TEST_MESSAGE_WITH_ATTACHMENT_ID=
MAILINATOR_TEST_ATTACHMENT_ID=
@@ -13,4 +15,6 @@ MAILINATOR_TEST_WEBHOOKTOKEN_CUSTOMSERVICE=
MAILINATOR_TEST_AUTH_SECRET=
MAILINATOR_TEST_AUTH_ID=
MAILINATOR_TEST_WEBHOOK_INBOX=
+# Set to 1 to run domain webhook tests, which create four messages.
+MAILINATOR_TEST_RUN_WEBHOOKS=0
MAILINATOR_TEST_WEBHOOK_CUSTOMSERVICE=
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 70829a1..18c11e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,41 @@ All notable changes to this project will be documented in this file.
The format is based on *Keep a Changelog* and this project aims to follow *Semantic Versioning*.
+## [1.0.8] - TBD
+
+### Added
+
+- Domain and inbox webhook injection via `PostWebhookMessageAsync` and `PostWebhookInboxMessageAsync`, supporting both webhook authentication forms, rich JSON payloads, offline unit tests, and opt-in integration tests.
+
+- `MessagesClient.GetMessageTextAsync`, `GetMessageTextPlainAsync`, and `GetMessageTextHtmlAsync` with request/response models, offline route and JSON deserialization tests, and opt-in read-only integration tests.
+
+- `MessagesClient.GetMessageSummaryAsync` with request/response models, offline request and deserialization tests, and an opt-in live test for an existing email.
+- `MessagesClient.GetMessageHeadersAsync` with request/response models, offline request and deserialization tests, and an opt-in live test for an existing email.
+- `MessagesClient.ListDomainMessagesAsync` and `ListDomainMessagesRequest` for domain-wide message listing, with optional inbox filtering and all documented listing parameters. Returns the existing `FetchInboxResponse` model.
+- Offline tests for domain listing query parameters, default behavior, and wildcard filtering.
+
+### Security
+
+- Updated the transitive `System.Text.Json` dependency to `10.0.11` to remediate CVE-2024-43485.
+
+### Changed
+
+- Updated `Newtonsoft.Json` to `13.0.4`.
+- Updated `RestSharp` to `114.0.0`.
+- Updated the OpenAPI coverage tool's `Microsoft.OpenApi.Readers` dependency to `1.6.31`.
+- Updated the offline unit-test stack to `Microsoft.NET.Test.Sdk` `18.9.0` and MSTest `4.4.0`.
+- Migrated the live integration-test project to SDK-style `PackageReference` and the same current test stack.
+- Updated integration-test exception assertions and binding redirects for MSTest 4 compatibility.
+- Added NuGet package lock files.
+
+### Deprecated
+
+- `AuthenticatorsClient.GetAuthenticatorsAsync`, `GetAuthenticatorAsync`, and `GetAuthenticatorByIdAsync`; use `GetAuthenticatorsByIdAsync` for the documented stored-authenticator operation.
+
+### Fixed
+
+- Forwarded the optional `delete` query parameter in `FetchInboxMessageAsync`.
+
## [1.0.7] - 2026-08-15
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..f860221
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,5 @@
+
+
+ true
+
+
diff --git a/EXAMPLES.md b/EXAMPLES.md
index 16341e9..0faafcd 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -43,7 +43,7 @@ var response = await client.MessagesClient.FetchInboxAsync(request);
## Authenticators
-Instant TOTP code + list authenticators:
+Instant TOTP code + get a stored authenticator:
```csharp
using mailinator_csharp_client;
@@ -54,8 +54,6 @@ var client = new MailinatorClient("yourApiTokenHere");
var totp = await client.AuthenticatorsClient.InstantTOTP2FACodeAsync(
new InstantTOTP2FACodeRequest { TotpSecretKey = "yourAuthSecret" });
-var authenticators = await client.AuthenticatorsClient.GetAuthenticatorsAsync();
-
var byId = await client.AuthenticatorsClient.GetAuthenticatorsByIdAsync(
new GetAuthenticatorsByIdRequest { Id = "yourAuthId" });
```
@@ -78,6 +76,73 @@ var domain = await client.DomainsClient.GetDomainAsync(
## Messages
+### List domain messages
+
+List messages across all inboxes in a domain:
+
+```csharp
+using mailinator_csharp_client.Models.Messages.Requests;
+
+// Uses the authenticated client created in Setup.
+var request = new ListDomainMessagesRequest
+{
+ Domain = "your_private_domain.com",
+ Limit = 20
+};
+
+var response = await client.MessagesClient.ListDomainMessagesAsync(request);
+var messages = response.Messages;
+
+// Fetch the next page only when the API supplies a cursor.
+if (!string.IsNullOrEmpty(response.Cursor))
+{
+ request.Cursor = response.Cursor;
+ var nextPage = await client.MessagesClient.ListDomainMessagesAsync(request);
+}
+```
+
+`Inbox` is an optional query filter: omit it or set it to `"*"` for all inboxes, or supply an inbox name/prefix such as `"orders*"`. The request also supports `Skip`, `Limit`, `Sort`, `DecodeSubject`, `Cursor`, `Full`, `Wait`, and `Delete`. Set `Full = true` to request full message content. `Delete` schedules deletion after retrieval (for example, `"30s"`); it is omitted by default.
+
+The result is a `FetchInboxResponse`, sharing the inbox-listing response model and pagination cursor.
+
+### Get message summary
+
+```csharp
+using mailinator_csharp_client.Models.Messages.Requests;
+
+// Uses the authenticated client created in Setup.
+var response = await client.MessagesClient.GetMessageSummaryAsync(
+ new GetMessageSummaryRequest
+ {
+ Domain = "your_private_domain.com",
+ MessageId = "your-message-id"
+ });
+
+var summary = response.Summary;
+```
+
+`Summary` reuses the `Message` model and contains the subject, domain, sender (`From`), message ID, recipient (`To`), and timestamp (`Time`). This endpoint does not return body or attachment content.
+
+### Get message headers
+
+```csharp
+using mailinator_csharp_client.Models.Messages.Requests;
+
+// Uses the authenticated client created in Setup.
+var response = await client.MessagesClient.GetMessageHeadersAsync(
+ new GetMessageHeadersRequest
+ {
+ Domain = "your_private_domain.com",
+ MessageId = "your-message-id"
+ });
+
+var headers = response.Headers;
+```
+
+Use a message ID returned by inbox or domain listing. `Headers` is a `Dictionary` that preserves custom header names. Values can be strings or JSON arrays (for example, `received`), matching the existing full-message header model.
+
+### Post a message
+
Post (inject) a message:
```csharp
@@ -218,3 +283,58 @@ var customServiceInboxWebhook = await client.WebhooksClient.PrivateCustomService
- Ensure you’re using an API token from your Mailinator team settings.
- For webhook injection, use webhook tokens (`whtoken`) instead of your API token.
+
+## Get message content
+
+Use a message ID returned by inbox or domain listing:
+
+```csharp
+var extracted = await client.MessagesClient.GetMessageTextAsync(
+ new GetMessageTextRequest { Domain = "your-private-domain.com", MessageId = "your-message-id" });
+var plain = await client.MessagesClient.GetMessageTextPlainAsync(
+ new GetMessageTextPlainRequest { Domain = "your-private-domain.com", MessageId = "your-message-id" });
+var html = await client.MessagesClient.GetMessageTextHtmlAsync(
+ new GetMessageTextHtmlRequest { Domain = "your-private-domain.com", MessageId = "your-message-id" });
+
+string extractedText = extracted.Text;
+string plainText = plain.TextPlain;
+string htmlBody = html.TextHtml;
+```
+
+These endpoints return JSON wrappers with `text`, `text/plain`, and `text/html` fields respectively. The SDK preserves their content, including HTML markup and any quoted-printable artifacts such as `=C2=A0` in extracted text. Empty strings are preserved. These operations do not delete the message.
+
+## Domain and inbox webhooks
+
+These endpoints authenticate with webhook tokens; no API token is needed. Request types are in `mailinator_csharp_client.Models.Webhooks.Requests` and `WebhookMessage` is in `mailinator_csharp_client.Models.Webhooks.Entities`.
+
+```csharp
+var client = new MailinatorClient();
+var webhookToken = Environment.GetEnvironmentVariable("MAILINATOR_WEBHOOK_TOKEN");
+var payload = new WebhookMessage
+{
+ To = "orders",
+ From = "sender@example.com",
+ Subject = "Order notification",
+ Text = "Order received",
+ Html = "Order received
"
+};
+
+var domainResult = await client.WebhooksClient.PostWebhookMessageAsync(
+ new PostWebhookMessageRequest
+ {
+ Domain = "your-private-domain.com",
+ WebhookToken = webhookToken,
+ Webhook = payload
+ });
+
+var inboxResult = await client.WebhooksClient.PostWebhookInboxMessageAsync(
+ new PostWebhookInboxMessageRequest
+ {
+ Domain = "your-private-domain.com",
+ Inbox = "orders",
+ WebhookToken = webhookToken,
+ Webhook = payload
+ });
+```
+
+Both methods also accept the webhook token in `Domain`; omit `WebhookToken` for that form. The payload's `To` field is required by the specification. `Headers` accepts a dictionary of string values, and `AdditionalProperties` accepts custom JSON fields. Both responses expose `Status` and `Id`. Existing private/custom-service webhook methods remain available.
diff --git a/README.md b/README.md
index 7266a6e..47f7ce0 100644
--- a/README.md
+++ b/README.md
@@ -54,8 +54,16 @@ var response = await client.MessagesClient.FetchInboxAsync(
All API operations are asynchronous and end in `Async`. Operations are grouped under `MessagesClient`, `DomainsClient`, `AuthenticatorsClient`, `StatsClient`, `WebhooksClient`, and `RulesClient`.
+To list messages across a domain, use `MessagesClient.ListDomainMessagesAsync(new ListDomainMessagesRequest { Domain = "your-private-domain.com" })`. The optional `Inbox` query filter scopes results to an inbox; omit it to include all inboxes. See the [domain listing example](EXAMPLES.md#list-domain-messages) for pagination.
+
## API reference
+Retrieve message metadata without the body with `MessagesClient.GetMessageSummaryAsync(new GetMessageSummaryRequest { Domain = "your-private-domain.com", MessageId = "your-message-id" })`. See the [message summary example](EXAMPLES.md#get-message-summary).
+
+Retrieve message content with `MessagesClient.GetMessageTextAsync`, `GetMessageTextPlainAsync`, or `GetMessageTextHtmlAsync`. See the [message content examples](EXAMPLES.md#get-message-content).
+
+Retrieve SMTP headers for an existing message with `MessagesClient.GetMessageHeadersAsync(new GetMessageHeadersRequest { Domain = "your-private-domain.com", MessageId = "your-message-id" })`. See the [header retrieval example](EXAMPLES.md#get-message-headers).
+
- [Mailinator API reference](https://www.mailinator.com/documentation/docs/api/index.html) describes the REST API.
- [REFERENCE.md](REFERENCE.md) lists the operations currently exposed by this SDK.
- [EXAMPLES.md](EXAMPLES.md) contains examples for common SDK workflows.
@@ -97,3 +105,5 @@ dotnet test mailinator-csharp-client-unit-tests/mailinator-csharp-client-unit-te
The separate legacy integration suite calls the live Mailinator API and requires deliberate account configuration. Some tests create or delete remote resources. Read [TESTING.md](TESTING.md) before running it.
To compare the SDK request surface with the OpenAPI specification, use the [OpenAPI coverage check](eng/README.md#openapi-coverage-check).
+
+Domain and inbox webhook injection is available through `WebhooksClient.PostWebhookMessageAsync` and `PostWebhookInboxMessageAsync`, using a webhook token with a tokenless `new MailinatorClient()`. See the [webhook examples](EXAMPLES.md#domain-and-inbox-webhooks).
diff --git a/REFERENCE.md b/REFERENCE.md
index 8f42743..d530dc0 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -18,6 +18,12 @@ An authenticated client exposes `MessagesClient`, `DomainsClient`, `Authenticato
| Operation | Request | Response |
| --- | --- | --- |
| `FetchInboxAsync` | `FetchInboxRequest` | `FetchInboxResponse` |
+| `ListDomainMessagesAsync` | `ListDomainMessagesRequest` | `FetchInboxResponse` |
+| `GetMessageHeadersAsync` | `GetMessageHeadersRequest` | `GetMessageHeadersResponse` |
+| `GetMessageSummaryAsync` | `GetMessageSummaryRequest` | `GetMessageSummaryResponse` |
+| `GetMessageTextAsync` | `GetMessageTextRequest` | `GetMessageTextResponse` |
+| `GetMessageTextPlainAsync` | `GetMessageTextPlainRequest` | `GetMessageTextPlainResponse` |
+| `GetMessageTextHtmlAsync` | `GetMessageTextHtmlRequest` | `GetMessageTextHtmlResponse` |
| `FetchInboxMessageAsync` | `FetchInboxMessageRequest` | `FetchInboxMessageResponse` |
| `FetchMessageAsync` | `FetchMessageRequest` | `FetchMessageResponse` |
| `FetchSMSMessagesAsync` | `FetchSMSMessagesRequest` | `FetchSMSMessagesResponse` |
@@ -74,6 +80,8 @@ These operations use webhook tokens rather than the API token supplied to `Maili
| Operation | Request | Response |
| --- | --- | --- |
+| `PostWebhookMessageAsync` | `PostWebhookMessageRequest` | `PostWebhookMessageResponse` |
+| `PostWebhookInboxMessageAsync` | `PostWebhookInboxMessageRequest` | `PostWebhookMessageResponse` |
| `PrivateWebhookAsync` | `PrivateWebhookRequest` | `PrivateWebhookResponse` |
| `PrivateInboxWebhookAsync` | `PrivateInboxWebhookRequest` | `PrivateWebhookResponse` |
| `PrivateCustomServiceWebhookAsync` | `PrivateCustomServiceWebhookRequest` | `PrivateCustomServiceWebhookResponse` |
@@ -100,6 +108,7 @@ The following public methods are marked `[Obsolete]`. They remain in the current
| --- | --- | --- |
| `MessagesClient` | `FetchLatestMessagesAsync`, `FetchLatestInboxMessagesAsync` | The wildcard “latest” endpoints are not in the current OpenAPI specification. |
| `DomainsClient` | `CreateDomainAsync`, `DeleteDomainAsync` | Domain create/delete endpoints are not in the current OpenAPI specification. |
+| `AuthenticatorsClient` | `GetAuthenticatorsAsync`, `GetAuthenticatorAsync`, `GetAuthenticatorByIdAsync` | These list/get endpoints are not in the current OpenAPI specification. |
| `RulesClient` | All operations | Rules endpoints are not in the current OpenAPI specification. |
See [ROADMAP.md](ROADMAP.md) for known specification gaps and planned alignment work.
diff --git a/ROADMAP.md b/ROADMAP.md
index c1e1f6b..86eec10 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -13,52 +13,24 @@ This document is a living roadmap for the Mailinator C# client. It’s intention
- Target frameworks: `net471`; `netstandard2.0`
- API coverage vs spec: see “Gap Analysis”
-- Known gaps / bugs: missing spec endpoints, SDK-only endpoints, path parameter-name mismatches, and one missing query parameter listed below.
+- Known gaps / bugs: missing spec endpoints and documented SDK-only compatibility operations. Path parameter-name differences and the SMS convenience alias are intentional and do not change the resulting HTTP route.
## Dependency Maintenance
-Audit refreshed: 2026-08-10.
+Dependencies were audited on 2026-09-04. All projects have a clean NuGet vulnerability audit, and committed package lock files make restores reproducible.
-Security status:
-
-- No current direct dependency or package listed in `mailinator-csharp-client-tests/packages.config` falls within a known advisory range in NuGet's vulnerability feed.
-- `RestSharp` `112.0.0` resolves `System.Text.Json` at `8.0.4` or newer for `net471` and `netstandard2.0`; the advisories currently listed for the 8.x line affect versions through `8.0.3`.
-- The repository has no lock files, and the .NET SDK is not available in the current audit environment, so a restored full transitive graph could not be verified with `dotnet list package --vulnerable --include-transitive`.
-
-Production and tooling dependencies:
-
-- `Newtonsoft.Json`: current `13.0.3`; latest stable `13.0.4`.
-- `RestSharp`: current `112.0.0`; latest stable `114.0.0`. Version `114.0.0` still supports `net471` and `netstandard2.0`, but raises its `System.Text.Json` dependency from `8.0.4` to `10.0.0` and requires API compatibility testing.
-- `Microsoft.OpenApi.Readers`: current `1.6.29`; latest stable `1.6.29` (2.x remains preview-only).
-
-Legacy test-project dependencies:
-
-- `Microsoft.ApplicationInsights`: `2.22.0` → `3.1.2`.
-- `Microsoft.Testing.Platform` and related extensions: `1.3.2` → `2.3.3`.
-- `Microsoft.TestPlatform.ObjectModel`: `17.10.0` → `18.8.1`.
-- `MSTest.TestAdapter` and `MSTest.TestFramework`: `3.5.2` → `4.3.3`.
-- Explicitly pinned support packages are also behind: `System.Buffers` (`4.5.1` → `4.6.1`), `System.Collections.Immutable` (`1.5.0` → `10.0.10`), `System.Diagnostics.DiagnosticSource` (`5.0.0` → `10.0.10`), `System.Memory` (`4.5.4` → `4.6.3`), `System.Numerics.Vectors` (`4.5.0` → `4.6.1`), `System.Reflection.Metadata` (`1.6.0` → `10.0.10`), and `System.Runtime.CompilerServices.Unsafe` (`5.0.0` → `6.1.2`).
-
-Work items:
-
-- Update `Newtonsoft.Json` to `13.0.4` and run build/tests.
-- Evaluate `RestSharp` `114.0.0` in a dedicated change; verify source compatibility, serialization behavior, all target frameworks, and the full SDK test suite.
-- Convert the legacy `net472` test project from `packages.config` to SDK-style `PackageReference`, then upgrade the Microsoft testing packages as one coordinated stack and remove direct pins for transitive `System.*` dependencies where possible.
-- Keep `Microsoft.OpenApi.Readers` on `1.6.29` until a stable 2.x release or a specific tooling requirement justifies a preview.
-- Add lock files and a CI dependency check (`dotnet list package --vulnerable --include-transitive`) after restore tooling is available.
-
-## Gap Analysis (2026-03-23)
+## Gap Analysis (2026-09-08)
This snapshot compares the SDK’s implemented operations to the Mailinator OpenAPI spec (`mailinator-api.yaml`).
- Spec operations: 35
-- SDK operations: 43
-- Exact matches: 21
-- Missing from SDK: 10
+- SDK operations: 51
+- Exact matches: 29
+- Missing from SDK: 2
- SDK-only (no spec match): 17
- SDK aliases / convenience wrappers: 1
- Path parameter-name mismatches: 4
-- Operations with missing query params: 1
+- Operations with missing query params: 0
Re-run locally:
@@ -70,52 +42,44 @@ Re-run locally:
Add these operations that exist in the spec but are missing from the SDK:
- **Messages**
- - `listDomainMessages` — `GET /api/v2/domains/{domain}/inboxes`
- - `getMessageHeaders` — `GET /api/v2/domains/{domain}/messages/{messageId}/headers`
- - `getMessageSummary` — `GET /api/v2/domains/{domain}/messages/{messageId}/summary`
- - `getMessageText` — `GET /api/v2/domains/{domain}/messages/{messageId}/text`
- - `getMessageTextHtml` — `GET /api/v2/domains/{domain}/messages/{messageId}/texthtml`
- - `getMessageTextPlain` — `GET /api/v2/domains/{domain}/messages/{messageId}/textplain`
- `streamDomainMessages` — `GET /api/v2/domains/{domain}/stream`
- `streamInboxMessages` — `GET /api/v2/domains/{domain}/stream/{inbox}`
-- **Webhook**
- - `postWebhookMessage` — `POST /api/v2/domains/{domain}/webhook`
- - `postWebhookInboxMessage` — `POST /api/v2/domains/{domain}/webhook/{inbox}`
### Work Items (SDK → spec)
-These SDK operations do not have a matching operation in the current OpenAPI spec. Decide for each group whether to (a) update the spec, (b) deprecate/remove the SDK surface, or (c) keep but document explicitly as “not in spec”.
+These SDK operations do not have a matching operation in the current OpenAPI spec. All remaining cases now have a compatibility decision recorded below.
+
+- **Webhooks** private/custom-service endpoints (`POST /api/v2/domains/private/...`) are intentional, supported compatibility APIs shared with the JavaScript client. Keep them documented and do not deprecate them solely because they are absent from the current spec.
-- **Rules** (6 operations under `/api/v2/domains/{domain_id}/rules...`)
-- **Domains** create/delete (`POST`/`DELETE /api/v2/domains/{domain_id}`)
-- **Authenticators** list/get variants (`/api/v2/authenticator...` and `/api/v2/authenticators`)
-- **Messages** “latest” wildcard endpoints (`GET .../messages/*`)
-- **Webhooks** private/custom-service endpoints (`POST /api/v2/domains/private/...`)
+### Resolved compatibility decisions
+
+The following operations are already deprecated. Retain them for source compatibility and skip further spec-alignment work; remove them only in a future breaking major release.
+
+- **Rules** — 6 operations under `/api/v2/domains/{domain_id}/rules...`
+- **Domains** — create/delete (`POST`/`DELETE /api/v2/domains/{domain_id}`)
+- **Authenticators** — the unsupported list/get variants under `/api/v2/authenticator...` and `/api/v2/authenticators` are deprecated; retain them only until a future breaking major release.
+- **Messages** — “latest” wildcard endpoints (`GET .../messages/*`)
### Work Items (spec alignment)
-Path template parameter names differ from the spec (non-breaking, but worth aligning for clarity and consistency):
+Path template parameter names differ from the spec. These are intentional, non-functional differences; the resulting HTTP routes are the same, so no SDK change is planned:
- Attachments: `{attachmentName}` (spec) vs `{attachmentId}` (SDK)
- Authenticators: `{authenticator_id}` (spec) vs `{auth_id}` (SDK)
- Domains: `{domain_name}` (spec) vs `{domain_id}` (SDK)
-Query parameters differ from the spec:
-
-- `GET /api/v2/domains/{domain}/inboxes/{inbox}/messages/{messageId}` is missing the optional `delete` query parameter in the SDK.
+`FetchSMSMessagesAsync` is also an intentional convenience alias for inbox retrieval using the team SMS number. `FetchInboxAsync` remains available when callers need the full inbox-listing parameter set.
## Near-Term (next 1–3 updates)
- Keep gap analysis up to date (re-run after changes).
- Decide on versioning and release cadence.
- Implement missing spec endpoints (see “Work Items (spec → SDK)”).
-- Resolve spec alignment issues (path template parameter names).
-- Make an explicit decision on SDK-only endpoints (spec update vs deprecate vs document).
- Improve docs: examples, configuration, troubleshooting.
## Mid-Term
-- Improve test coverage and add integration test guidance.
+- Improve test coverage.
- Add more ergonomic APIs / helpers while keeping the low-level request mapping.
## Long-Term
diff --git a/TESTING.md b/TESTING.md
index b829c7d..e50e4a7 100644
--- a/TESTING.md
+++ b/TESTING.md
@@ -4,7 +4,7 @@ The repository has two distinct MSTest suites: fast offline unit tests and legac
## Offline unit tests
-The .NET 8 unit-test project verifies request construction without making network calls:
+The .NET 8 unit-test project verifies request construction for every public async SDK operation without making network calls. It asserts each operation's HTTP method, route, path/query parameters, and JSON body where applicable:
```sh
dotnet test mailinator-csharp-client-unit-tests/mailinator-csharp-client-unit-tests.csproj
@@ -33,6 +33,7 @@ The `.env` file is excluded from Git. Process environment variables take precede
| `MAILINATOR_TEST_API_TOKEN` | API token used by authenticated integration tests. |
| `MAILINATOR_TEST_DOMAIN_PRIVATE` | Private domain used by domain and message tests. |
| `MAILINATOR_TEST_INBOX` | Existing inbox in the configured private domain. |
+| `MAILINATOR_TEST_MESSAGE_ID` | Existing email in the configured private domain, for headers, summary, and content retrieval. Content tests require nonempty plain-text and HTML MIME parts; header retrieval requires SMTP headers. |
| `MAILINATOR_TEST_PHONE_NUMBER` | Team SMS number whose messages can be fetched. |
| `MAILINATOR_TEST_MESSAGE_WITH_ATTACHMENT_ID` | ID of an existing message that has an attachment. |
| `MAILINATOR_TEST_ATTACHMENT_ID` | Attachment ID belonging to the configured message. |
@@ -51,3 +52,55 @@ dotnet test mailinator-csharp-client-tests/mailinator-csharp-client-tests.csproj
```
Prefer a test filter when validating a specific endpoint, especially for operations that mutate remote state.
+
+To test header retrieval only, configure `MAILINATOR_TEST_API_TOKEN`, `MAILINATOR_TEST_DOMAIN_PRIVATE`, and `MAILINATOR_TEST_MESSAGE_ID`, then run:
+
+```sh
+dotnet test mailinator-csharp-client-tests/mailinator-csharp-client-tests.csproj --filter "FullyQualifiedName=mailinator_csharp_client_tests.MessagesEndpointTests.GetMessageHeadersAsync"
+```
+
+Use an existing email received through SMTP with headers; an attachment is not required. This test only reads the message headers and does not create or delete messages. Missing configuration marks the test inconclusive.
+
+Summary retrieval uses the same three environment variables and reads the existing message without creating or deleting data:
+
+```sh
+dotnet test mailinator-csharp-client-tests/mailinator-csharp-client-tests.csproj --filter "FullyQualifiedName=mailinator_csharp_client_tests.MessagesEndpointTests.GetMessageSummaryAsync"
+```
+
+### Message content endpoints
+
+The three tests in `MessageContentEndpointTests` only read an existing email. Configure `MAILINATOR_TEST_API_TOKEN`, `MAILINATOR_TEST_DOMAIN_PRIVATE`, and `MAILINATOR_TEST_MESSAGE_ID`. Use an SMTP email containing both nonempty `text/plain` and `text/html` MIME parts. Missing configuration marks tests inconclusive; an unsuitable or expired fixture fails the content assertions.
+
+Run the focused offline tests:
+
+```sh
+dotnet test mailinator-csharp-client-unit-tests/mailinator-csharp-client-unit-tests.csproj --filter "FullyQualifiedName~MessageContentTests"
+```
+
+Run all offline tests using the unfiltered command in the first section, then run the new read-only integration tests on a compatible Windows machine:
+
+```sh
+dotnet test mailinator-csharp-client-tests/mailinator-csharp-client-tests.csproj --filter "FullyQualifiedName~MessageContentEndpointTests"
+```
+
+To also validate the existing header and summary endpoints without selecting other message tests:
+
+```sh
+dotnet test mailinator-csharp-client-tests/mailinator-csharp-client-tests.csproj --filter "FullyQualifiedName~MessageContentEndpointTests|FullyQualifiedName=mailinator_csharp_client_tests.MessagesEndpointTests.GetMessageHeadersAsync|FullyQualifiedName=mailinator_csharp_client_tests.MessagesEndpointTests.GetMessageSummaryAsync"
+```
+
+### Domain and inbox webhooks
+
+Offline tests cover both authentication forms, request bodies, optional token queries, custom payload fields, and response deserialization:
+
+```sh
+dotnet test mailinator-csharp-client-unit-tests/mailinator-csharp-client-unit-tests.csproj --filter "FullyQualifiedName~WebhookMessageTests"
+```
+
+`DomainWebhookEndpointTests` loads the repository `.env` with process environment variables taking precedence. It does not require an API token. Set `MAILINATOR_TEST_DOMAIN_PRIVATE`, `MAILINATOR_TEST_WEBHOOKTOKEN_PRIVATEDOMAIN`, and `MAILINATOR_TEST_WEBHOOK_INBOX` for a test domain. Set `MAILINATOR_TEST_RUN_WEBHOOKS=1` to opt in. Without the opt-in or required configuration, tests are inconclusive.
+
+```sh
+dotnet test mailinator-csharp-client-tests/mailinator-csharp-client-tests.csproj --filter "FullyQualifiedName~DomainWebhookEndpointTests"
+```
+
+This runs four cases: domain and inbox injection with query-token and path-token authentication. Each creates one message and checks the acceptance status and message ID; it does not verify subsequent delivery or delete messages.
diff --git a/eng/OpenApiCoverageCheck/OpenApiCoverageCheck.csproj b/eng/OpenApiCoverageCheck/OpenApiCoverageCheck.csproj
index 8a4f8a9..7178212 100644
--- a/eng/OpenApiCoverageCheck/OpenApiCoverageCheck.csproj
+++ b/eng/OpenApiCoverageCheck/OpenApiCoverageCheck.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/eng/OpenApiCoverageCheck/packages.lock.json b/eng/OpenApiCoverageCheck/packages.lock.json
new file mode 100644
index 0000000..a717b22
--- /dev/null
+++ b/eng/OpenApiCoverageCheck/packages.lock.json
@@ -0,0 +1,27 @@
+{
+ "version": 1,
+ "dependencies": {
+ "net8.0": {
+ "Microsoft.OpenApi.Readers": {
+ "type": "Direct",
+ "requested": "[1.6.31, )",
+ "resolved": "1.6.31",
+ "contentHash": "MbtHhR2vYs11EE71ywvmyppzp0noC3XHKzdI6m/9mwKRzw4IVCGuHHWDnmjr1BX90CIOOxbz6WszBJt3a+WGBg==",
+ "dependencies": {
+ "Microsoft.OpenApi": "1.6.31",
+ "SharpYaml": "2.1.5"
+ }
+ },
+ "Microsoft.OpenApi": {
+ "type": "Transitive",
+ "resolved": "1.6.31",
+ "contentHash": "58ggc22i0BKvHuVoj5oVVQ7EbMkEMY0FvGeoQ/yBemk36pUbroDoj1XxRTnCi+fsgDTDuf+YZmigHkEzs5B2wA=="
+ },
+ "SharpYaml": {
+ "type": "Transitive",
+ "resolved": "2.1.5",
+ "contentHash": "1pNSpfXxNAgCQ9sacKKpEWO2DxTefoLEjw0v1nWMvyJScUWacoHvjXr4LfcUkhm0qYgNYripFFiUeEzW4tdXIQ=="
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/mailinator-csharp-client-tests/DomainWebhookEndpointTests.cs b/mailinator-csharp-client-tests/DomainWebhookEndpointTests.cs
new file mode 100644
index 0000000..081dade
--- /dev/null
+++ b/mailinator-csharp-client-tests/DomainWebhookEndpointTests.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Threading.Tasks;
+using mailinator_csharp_client;
+using mailinator_csharp_client.Helpers;
+using mailinator_csharp_client.Models.Webhooks.Entities;
+using mailinator_csharp_client.Models.Webhooks.Requests;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace mailinator_csharp_client_tests
+{
+ // Explicitly opt in: each test case creates one message and does not delete it.
+ [TestClass]
+ public class DomainWebhookEndpointTests
+ {
+ [TestInitialize]
+ public void LoadConfiguration()
+ {
+ TestEnvironment.LoadDotEnv();
+ }
+
+ [TestMethod, TestCategory("Webhooks.PostWebhookMessageAsync")]
+ [DataRow(false)]
+ [DataRow(true)]
+ public async Task PostWebhookMessageAsync(bool tokenInPath)
+ {
+ if (Environment.GetEnvironmentVariable("MAILINATOR_TEST_RUN_WEBHOOKS") != "1")
+ Assert.Inconclusive("Set MAILINATOR_TEST_RUN_WEBHOOKS=1 to authorize creating test messages.");
+ var domain = Environment.GetEnvironmentVariable("MAILINATOR_TEST_DOMAIN_PRIVATE");
+ var token = Environment.GetEnvironmentVariable("MAILINATOR_TEST_WEBHOOKTOKEN_PRIVATEDOMAIN");
+ var inbox = Environment.GetEnvironmentVariable("MAILINATOR_TEST_WEBHOOK_INBOX");
+ if (string.IsNullOrWhiteSpace(token) || string.IsNullOrWhiteSpace(inbox) ||
+ (!tokenInPath && string.IsNullOrWhiteSpace(domain)))
+ Assert.Inconclusive("Configure the private domain, webhook token, and webhook inbox process environment variables.");
+
+ var client = new MailinatorClient();
+ try
+ {
+ var response = await client.WebhooksClient.PostWebhookMessageAsync(new PostWebhookMessageRequest
+ {
+ Domain = tokenInPath ? token : domain,
+ WebhookToken = tokenInPath ? null : token,
+ Webhook = new WebhookMessage
+ {
+ To = inbox, From = "sdk-test@example.com",
+ Subject = "C# webhook test " + Guid.NewGuid().ToString("N"),
+ Text = "Webhook integration test", Html = "Webhook integration test
"
+ }
+ });
+ Assert.IsNotNull(response);
+ Assert.IsTrue(response.Status == "ok", "The webhook should be accepted.");
+ Assert.IsFalse(string.IsNullOrWhiteSpace(response.Id), "The response should include a message ID.");
+ }
+ catch (ApiException exception)
+ {
+ // Never include token-bearing URLs or API response bodies in test output.
+ Assert.Fail($"Webhook injection failed with HTTP status {(int)exception.HttpStatusCode}.");
+ }
+ }
+
+ [TestMethod, TestCategory("Webhooks.PostWebhookInboxMessageAsync")]
+ [DataRow(false)]
+ [DataRow(true)]
+ public async Task PostWebhookInboxMessageAsync(bool tokenInPath)
+ {
+ if (Environment.GetEnvironmentVariable("MAILINATOR_TEST_RUN_WEBHOOKS") != "1")
+ Assert.Inconclusive("Set MAILINATOR_TEST_RUN_WEBHOOKS=1 to authorize creating test messages.");
+ var domain = Environment.GetEnvironmentVariable("MAILINATOR_TEST_DOMAIN_PRIVATE");
+ var token = Environment.GetEnvironmentVariable("MAILINATOR_TEST_WEBHOOKTOKEN_PRIVATEDOMAIN");
+ var inbox = Environment.GetEnvironmentVariable("MAILINATOR_TEST_WEBHOOK_INBOX");
+ if (string.IsNullOrWhiteSpace(token) || string.IsNullOrWhiteSpace(inbox) ||
+ (!tokenInPath && string.IsNullOrWhiteSpace(domain)))
+ Assert.Inconclusive("Configure the private domain, webhook token, and webhook inbox process environment variables.");
+
+ var client = new MailinatorClient();
+ try
+ {
+ var response = await client.WebhooksClient.PostWebhookInboxMessageAsync(new PostWebhookInboxMessageRequest
+ {
+ Domain = tokenInPath ? token : domain,
+ WebhookToken = tokenInPath ? null : token,
+ Inbox = inbox,
+ Webhook = new WebhookMessage
+ {
+ To = inbox, From = "sdk-test@example.com",
+ Subject = "C# webhook test " + Guid.NewGuid().ToString("N"),
+ Text = "Webhook integration test", Html = "Webhook integration test
"
+ }
+ });
+ Assert.IsNotNull(response);
+ Assert.IsTrue(response.Status == "ok", "The webhook should be accepted.");
+ Assert.IsFalse(string.IsNullOrWhiteSpace(response.Id), "The response should include a message ID.");
+ }
+ catch (ApiException exception)
+ {
+ // Never include token-bearing URLs or API response bodies in test output.
+ Assert.Fail($"Webhook injection failed with HTTP status {(int)exception.HttpStatusCode}.");
+ }
+ }
+
+ }
+}
diff --git a/mailinator-csharp-client-tests/MessageContentEndpointTests.cs b/mailinator-csharp-client-tests/MessageContentEndpointTests.cs
new file mode 100644
index 0000000..0e44cff
--- /dev/null
+++ b/mailinator-csharp-client-tests/MessageContentEndpointTests.cs
@@ -0,0 +1,79 @@
+using System.Threading.Tasks;
+using mailinator_csharp_client.Helpers;
+using mailinator_csharp_client.Models.Messages.Requests;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace mailinator_csharp_client_tests
+{
+ // Read-only tests against an existing SMTP email with plain-text and HTML parts.
+ [TestClass]
+ public class MessageContentEndpointTests : TestBase
+ {
+ [TestMethod, TestCategory("Messages.GetMessageTextAsync")]
+ public async Task GetMessageTextAsync()
+ {
+ if (string.IsNullOrWhiteSpace(PrivateDomain) || string.IsNullOrWhiteSpace(MessageId))
+ Assert.Inconclusive("Set MAILINATOR_TEST_DOMAIN_PRIVATE and MAILINATOR_TEST_MESSAGE_ID for an existing multipart email.");
+
+ try
+ {
+ var response = await mailinatorClient.MessagesClient.GetMessageTextAsync(
+ new GetMessageTextRequest { Domain = PrivateDomain, MessageId = MessageId });
+
+ Assert.IsNotNull(response);
+ Assert.IsFalse(string.IsNullOrWhiteSpace(response.Text),
+ "The configured email must contain nonempty text content.");
+ }
+ catch (ApiException exception)
+ {
+ // Do not expose message content or API error bodies in test output.
+ Assert.Fail($"Message content retrieval failed with HTTP status {(int)exception.HttpStatusCode}.");
+ }
+ }
+
+ [TestMethod, TestCategory("Messages.GetMessageTextPlainAsync")]
+ public async Task GetMessageTextPlainAsync()
+ {
+ if (string.IsNullOrWhiteSpace(PrivateDomain) || string.IsNullOrWhiteSpace(MessageId))
+ Assert.Inconclusive("Set MAILINATOR_TEST_DOMAIN_PRIVATE and MAILINATOR_TEST_MESSAGE_ID for an existing multipart email.");
+
+ try
+ {
+ var response = await mailinatorClient.MessagesClient.GetMessageTextPlainAsync(
+ new GetMessageTextPlainRequest { Domain = PrivateDomain, MessageId = MessageId });
+
+ Assert.IsNotNull(response);
+ Assert.IsFalse(string.IsNullOrWhiteSpace(response.TextPlain),
+ "The configured email must contain nonempty textplain content.");
+ }
+ catch (ApiException exception)
+ {
+ // Do not expose message content or API error bodies in test output.
+ Assert.Fail($"Message content retrieval failed with HTTP status {(int)exception.HttpStatusCode}.");
+ }
+ }
+
+ [TestMethod, TestCategory("Messages.GetMessageTextHtmlAsync")]
+ public async Task GetMessageTextHtmlAsync()
+ {
+ if (string.IsNullOrWhiteSpace(PrivateDomain) || string.IsNullOrWhiteSpace(MessageId))
+ Assert.Inconclusive("Set MAILINATOR_TEST_DOMAIN_PRIVATE and MAILINATOR_TEST_MESSAGE_ID for an existing multipart email.");
+
+ try
+ {
+ var response = await mailinatorClient.MessagesClient.GetMessageTextHtmlAsync(
+ new GetMessageTextHtmlRequest { Domain = PrivateDomain, MessageId = MessageId });
+
+ Assert.IsNotNull(response);
+ Assert.IsFalse(string.IsNullOrWhiteSpace(response.TextHtml),
+ "The configured email must contain nonempty texthtml content.");
+ }
+ catch (ApiException exception)
+ {
+ // Do not expose message content or API error bodies in test output.
+ Assert.Fail($"Message content retrieval failed with HTTP status {(int)exception.HttpStatusCode}.");
+ }
+ }
+
+ }
+}
diff --git a/mailinator-csharp-client-tests/MessagesEndpointTests.cs b/mailinator-csharp-client-tests/MessagesEndpointTests.cs
index 2ae60cd..1ada1b7 100644
--- a/mailinator-csharp-client-tests/MessagesEndpointTests.cs
+++ b/mailinator-csharp-client-tests/MessagesEndpointTests.cs
@@ -12,6 +12,83 @@ namespace mailinator_csharp_client_tests
[TestClass]
public class MessagesEndpointTests : TestBase
{
+ [TestMethod, TestCategory("Messages.GetMessageSummaryAsync")]
+ public async Task GetMessageSummaryAsync()
+ {
+ if (string.IsNullOrWhiteSpace(PrivateDomain) || string.IsNullOrWhiteSpace(MessageId))
+ Assert.Inconclusive("Set MAILINATOR_TEST_DOMAIN_PRIVATE and MAILINATOR_TEST_MESSAGE_ID for an existing email.");
+
+ mailinator_csharp_client.Models.Responses.GetMessageSummaryResponse response;
+ try
+ {
+ response = await mailinatorClient.MessagesClient.GetMessageSummaryAsync(
+ new GetMessageSummaryRequest { Domain = PrivateDomain, MessageId = MessageId });
+ }
+ catch (ApiException exception)
+ {
+ // Do not include potentially sensitive response bodies in test output.
+ Assert.Fail($"Message summary retrieval failed with HTTP status {(int)exception.HttpStatusCode}.");
+ return;
+ }
+
+ Assert.IsNotNull(response);
+ Assert.IsNotNull(response.Summary);
+ Assert.IsTrue(response.Summary.Id == MessageId, "The summary should identify the requested message.");
+ // Domain assertion deferred: the live API currently reports "public" for private-domain messages.
+ // Assert.IsTrue(response.Summary.Domain == PrivateDomain, "The summary should identify the requested domain.");
+ Assert.IsNull(response.Summary.Parts);
+ Assert.IsNull(response.Summary.Text);
+ }
+
+ [TestMethod, TestCategory("Messages.GetMessageHeadersAsync")]
+ public async Task GetMessageHeadersAsync()
+ {
+ if (string.IsNullOrWhiteSpace(PrivateDomain) || string.IsNullOrWhiteSpace(MessageId))
+ Assert.Inconclusive("Set MAILINATOR_TEST_DOMAIN_PRIVATE and MAILINATOR_TEST_MESSAGE_ID for an existing email.");
+
+ mailinator_csharp_client.Models.Responses.GetMessageHeadersResponse response;
+ try
+ {
+ response = await mailinatorClient.MessagesClient.GetMessageHeadersAsync(
+ new GetMessageHeadersRequest { Domain = PrivateDomain, MessageId = MessageId });
+ }
+ catch (ApiException exception)
+ {
+ // Do not include potentially sensitive response bodies in test output.
+ Assert.Fail($"Message headers retrieval failed with HTTP status {(int)exception.HttpStatusCode}.");
+ return;
+ }
+
+ Assert.IsNotNull(response);
+ Assert.IsNotNull(response.Headers);
+ Assert.IsTrue(response.Headers.Count > 0, "The configured email should have SMTP headers.");
+ }
+
+ [TestMethod, TestCategory("Messages.ListDomainMessagesAsync")]
+ public async Task ListDomainMessagesAsync()
+ {
+ if (string.IsNullOrWhiteSpace(PrivateDomain))
+ Assert.Inconclusive("Set MAILINATOR_TEST_DOMAIN_PRIVATE to run the domain listing integration test.");
+
+ var request = new ListDomainMessagesRequest { Domain = PrivateDomain, Limit = 2 };
+ mailinator_csharp_client.Models.Responses.FetchInboxResponse response;
+ try
+ {
+ response = await mailinatorClient.MessagesClient.ListDomainMessagesAsync(request);
+ }
+ catch (ApiException exception)
+ {
+ // API error bodies can contain private message data; report only the status.
+ Assert.Fail($"Domain listing failed with HTTP status {(int)exception.HttpStatusCode}.");
+ return;
+ }
+
+ Assert.IsNotNull(response);
+ Assert.IsTrue(response.Domain == PrivateDomain, "The response should identify the requested domain.");
+ Assert.IsNotNull(response.Messages);
+ Assert.IsTrue(response.Messages.Count <= request.Limit, "The result should respect the requested limit.");
+ }
+
[TestMethod, TestCategory("Messages.PostMessageAsync")]
public async Task PostMessageAsync()
{
@@ -129,7 +206,7 @@ public async Task FetchMessageWithDeleteQueueParamsAsync()
Thread.Sleep(45 * 1000);
- var exception = await Assert.ThrowsExceptionAsync(async () =>
+ var exception = await Assert.ThrowsExactlyAsync(async () =>
{
await mailinatorClient.MessagesClient.FetchMessageAsync(request);
});
@@ -140,7 +217,7 @@ public async Task FetchMessageWithDeleteQueueParamsAsync()
public async Task FetchMessageWhenMessageDoesNotExistAsync()
{
var request = new FetchMessageRequest() { Domain = PrivateDomain, MessageId = DateTime.UtcNow.Ticks.ToString() };
- var exception = await Assert.ThrowsExceptionAsync(async () =>
+ var exception = await Assert.ThrowsExactlyAsync(async () =>
{
await mailinatorClient.MessagesClient.FetchMessageAsync(request);
});
@@ -165,7 +242,7 @@ public async Task FetchInboxMessageAsync()
public async Task FetchInboxMessageWhenMessageDoesNotExistAsync()
{
var request = new FetchInboxMessageRequest() { Domain = PrivateDomain, Inbox = PrivateInbox, MessageId = DateTime.UtcNow.Ticks.ToString() };
- var exception = await Assert.ThrowsExceptionAsync(async () =>
+ var exception = await Assert.ThrowsExactlyAsync(async () =>
{
await mailinatorClient.MessagesClient.FetchInboxMessageAsync(request);
});
diff --git a/mailinator-csharp-client-tests/TestBase.cs b/mailinator-csharp-client-tests/TestBase.cs
index 78ef528..f53eae7 100644
--- a/mailinator-csharp-client-tests/TestBase.cs
+++ b/mailinator-csharp-client-tests/TestBase.cs
@@ -1,4 +1,4 @@
-using mailinator_csharp_client;
+using mailinator_csharp_client;
using mailinator_csharp_client.Models.Domains.Entities;
using mailinator_csharp_client.Models.Domains.Requests;
using mailinator_csharp_client.Models.Domains.Responses;
@@ -17,7 +17,6 @@
namespace mailinator_csharp_client_tests
{
- [TestClass]
public class TestBase
{
protected MailinatorClient mailinatorClient;
@@ -27,6 +26,7 @@ public class TestBase
private const string ENV_API_TOKEN = "MAILINATOR_TEST_API_TOKEN";
private const string ENV_DOMAIN_PRIVATE = "MAILINATOR_TEST_DOMAIN_PRIVATE";
private const string ENV_INBOX = "MAILINATOR_TEST_INBOX";
+ private const string ENV_MESSAGE_ID = "MAILINATOR_TEST_MESSAGE_ID";
private const string ENV_PHONE_NUMBER = "MAILINATOR_TEST_PHONE_NUMBER";
private const string ENV_MESSAGE_WITH_ATTACHMENT_ID = "MAILINATOR_TEST_MESSAGE_WITH_ATTACHMENT_ID";
private const string ENV_ATTACHMENT_ID = "MAILINATOR_TEST_ATTACHMENT_ID";
@@ -42,7 +42,7 @@ public class TestBase
static TestBase()
{
- LoadDotEnv();
+ TestEnvironment.LoadDotEnv();
ApiToken = GetEnvironmentVariable(ENV_API_TOKEN);
}
@@ -51,6 +51,7 @@ protected TestBase()
PrivateDomain = GetEnvironmentVariable(ENV_DOMAIN_PRIVATE);
DeleteDomain = GetEnvironmentVariable(ENV_DELETE_DOMAIN);
PrivateInbox = GetEnvironmentVariable(ENV_INBOX);
+ MessageId = GetEnvironmentVariable(ENV_MESSAGE_ID);
InboxAll = "*";
MessageIdWithAttachment = GetEnvironmentVariable(ENV_MESSAGE_WITH_ATTACHMENT_ID);
TeamSMSNumber = GetEnvironmentVariable(ENV_PHONE_NUMBER);
@@ -82,6 +83,7 @@ protected Domain Domain
}
protected string PrivateInbox { get; }
+ protected string MessageId { get; }
protected string PrivateDomain { get; }
protected string DeleteDomain { get; }
protected string InboxAll { get; }
@@ -163,51 +165,5 @@ private static string GetEnvironmentVariable(string name)
return Environment.GetEnvironmentVariable(name);
}
- private static void LoadDotEnv()
- {
- var dotEnvPath = FindDotEnv(Environment.CurrentDirectory) ?? FindDotEnv(AppDomain.CurrentDomain.BaseDirectory);
- if (dotEnvPath == null)
- return;
-
- foreach (var line in File.ReadAllLines(dotEnvPath))
- {
- var trimmedLine = line.Trim();
- if (trimmedLine.Length == 0 || trimmedLine.StartsWith("#"))
- continue;
-
- if (trimmedLine.StartsWith("export "))
- trimmedLine = trimmedLine.Substring("export ".Length).TrimStart();
-
- var separatorIndex = trimmedLine.IndexOf('=');
- if (separatorIndex <= 0)
- continue;
-
- var name = trimmedLine.Substring(0, separatorIndex).Trim();
- var value = trimmedLine.Substring(separatorIndex + 1).Trim();
- if (value.Length >= 2 && ((value.StartsWith("\"") && value.EndsWith("\"")) || (value.StartsWith("'") && value.EndsWith("'"))))
- value = value.Substring(1, value.Length - 2);
-
- if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(name)))
- Environment.SetEnvironmentVariable(name, value);
- }
- }
-
- private static string FindDotEnv(string startDirectory)
- {
- if (string.IsNullOrWhiteSpace(startDirectory))
- return null;
-
- var directory = new DirectoryInfo(startDirectory);
- while (directory != null)
- {
- var path = Path.Combine(directory.FullName, ".env");
- if (File.Exists(path))
- return path;
-
- directory = directory.Parent;
- }
-
- return null;
- }
}
}
diff --git a/mailinator-csharp-client-tests/TestEnvironment.cs b/mailinator-csharp-client-tests/TestEnvironment.cs
new file mode 100644
index 0000000..87195f7
--- /dev/null
+++ b/mailinator-csharp-client-tests/TestEnvironment.cs
@@ -0,0 +1,55 @@
+using System;
+using System.IO;
+
+namespace mailinator_csharp_client_tests
+{
+ internal static class TestEnvironment
+ {
+ internal static void LoadDotEnv()
+ {
+ var dotEnvPath = FindDotEnv(Environment.CurrentDirectory) ?? FindDotEnv(AppDomain.CurrentDomain.BaseDirectory);
+ if (dotEnvPath == null)
+ return;
+
+ foreach (var line in File.ReadAllLines(dotEnvPath))
+ {
+ var trimmedLine = line.Trim();
+ if (trimmedLine.Length == 0 || trimmedLine.StartsWith("#"))
+ continue;
+
+ if (trimmedLine.StartsWith("export "))
+ trimmedLine = trimmedLine.Substring("export ".Length).TrimStart();
+
+ var separatorIndex = trimmedLine.IndexOf('=');
+ if (separatorIndex <= 0)
+ continue;
+
+ var name = trimmedLine.Substring(0, separatorIndex).Trim();
+ var value = trimmedLine.Substring(separatorIndex + 1).Trim();
+ if (value.Length >= 2 && ((value.StartsWith("\"") && value.EndsWith("\"")) || (value.StartsWith("'") && value.EndsWith("'"))))
+ value = value.Substring(1, value.Length - 2);
+
+ if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(name)))
+ Environment.SetEnvironmentVariable(name, value);
+ }
+ }
+
+ private static string FindDotEnv(string startDirectory)
+ {
+ if (string.IsNullOrWhiteSpace(startDirectory))
+ return null;
+
+ var directory = new DirectoryInfo(startDirectory);
+ while (directory != null)
+ {
+ var path = Path.Combine(directory.FullName, ".env");
+ if (File.Exists(path))
+ return path;
+
+ directory = directory.Parent;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/mailinator-csharp-client-tests/app.config b/mailinator-csharp-client-tests/app.config
index f10b4ac..437e2f6 100644
--- a/mailinator-csharp-client-tests/app.config
+++ b/mailinator-csharp-client-tests/app.config
@@ -4,8 +4,8 @@
-
+
-
\ No newline at end of file
+
diff --git a/mailinator-csharp-client-tests/mailinator-csharp-client-tests.csproj b/mailinator-csharp-client-tests/mailinator-csharp-client-tests.csproj
index 0706128..305c93f 100644
--- a/mailinator-csharp-client-tests/mailinator-csharp-client-tests.csproj
+++ b/mailinator-csharp-client-tests/mailinator-csharp-client-tests.csproj
@@ -1,145 +1,24 @@
-
-
-
-
-
-
-
+
+
- Debug
- AnyCPU
- {D59A67E5-DE4B-49AF-BD14-143091B46527}
- Library
- Properties
+ net472
mailinator_csharp_client_tests
mailinator-csharp-client-tests
- v4.7.2
- 512
- {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- 15.0
- $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
- $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
- False
- UnitTest
-
-
-
+ false
+ true
+ false
+ true
+ true
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
- ..\packages\Microsoft.ApplicationInsights.2.22.0\lib\net46\Microsoft.ApplicationInsights.dll
-
-
-
- ..\packages\Microsoft.Testing.Extensions.Telemetry.1.3.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll
-
-
- ..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.3.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll
-
-
- ..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.3.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll
-
-
- ..\packages\Microsoft.Testing.Platform.1.3.2\lib\netstandard2.0\Microsoft.Testing.Platform.dll
-
-
- ..\packages\Microsoft.Testing.Platform.MSBuild.1.3.2\lib\netstandard2.0\Microsoft.Testing.Platform.MSBuild.dll
-
-
- ..\packages\Microsoft.TestPlatform.ObjectModel.17.10.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll
-
-
- ..\packages\Microsoft.TestPlatform.ObjectModel.17.10.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll
-
-
- ..\packages\Microsoft.TestPlatform.ObjectModel.17.10.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll
-
-
- ..\packages\MSTest.TestFramework.3.5.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
-
-
- ..\packages\MSTest.TestFramework.3.5.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
-
-
-
- ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll
-
-
- ..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll
-
-
-
-
- ..\packages\System.Diagnostics.DiagnosticSource.5.0.0\lib\net46\System.Diagnostics.DiagnosticSource.dll
-
-
- ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll
-
-
-
-
- ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll
-
-
- ..\packages\System.Reflection.Metadata.1.6.0\lib\netstandard2.0\System.Reflection.Metadata.dll
-
-
-
- ..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
-
- {7ab3b7cd-7633-4de4-8b8b-4a3b3924e085}
- mailinator-csharp-client
-
+
-
-
-
-
- This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
diff --git a/mailinator-csharp-client-tests/packages.config b/mailinator-csharp-client-tests/packages.config
deleted file mode 100644
index f8adf0b..0000000
--- a/mailinator-csharp-client-tests/packages.config
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mailinator-csharp-client-tests/packages.lock.json b/mailinator-csharp-client-tests/packages.lock.json
new file mode 100644
index 0000000..6fc2828
--- /dev/null
+++ b/mailinator-csharp-client-tests/packages.lock.json
@@ -0,0 +1,228 @@
+{
+ "version": 1,
+ "dependencies": {
+ ".NETFramework,Version=v4.7.2": {
+ "Microsoft.NET.Test.Sdk": {
+ "type": "Direct",
+ "requested": "[18.9.0, )",
+ "resolved": "18.9.0",
+ "contentHash": "xIzVXa/VpKXqDWzyC/5Hw8JbFY5u9k3Jc53CpcjBiOXvkQGio484LD9FNwOiGUSMDcYL3Rcw9sNgGi5WrswlPQ==",
+ "dependencies": {
+ "Microsoft.CodeCoverage": "18.9.0"
+ }
+ },
+ "MSTest.TestAdapter": {
+ "type": "Direct",
+ "requested": "[4.4.0, )",
+ "resolved": "4.4.0",
+ "contentHash": "xZdlCr2r/6XFOo67jcemJsfm46tTgXdqtnISFIuDUAJf5Yxk07XpTC9eMUErLdlYD8QM9zw07eOPre7+nTayUg==",
+ "dependencies": {
+ "MSTest.TestFramework": "4.4.0",
+ "Microsoft.TestPlatform.ObjectModel": "18.9.0",
+ "Microsoft.Testing.Extensions.Telemetry": "2.4.0",
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.4.0",
+ "Microsoft.Testing.Platform.MSBuild": "2.4.0",
+ "System.Memory": "4.6.3",
+ "System.Threading.Tasks.Extensions": "4.5.4"
+ }
+ },
+ "MSTest.TestFramework": {
+ "type": "Direct",
+ "requested": "[4.4.0, )",
+ "resolved": "4.4.0",
+ "contentHash": "sfJzj3ntdU/GahJd2n+WQLgKqbV0RRMJVXtjRwRQLhblevlzLSDAOocOKANhfMaCEQ5dMt6348ztbVq/Ap60xA==",
+ "dependencies": {
+ "MSTest.Analyzers": "4.4.0"
+ }
+ },
+ "Microsoft.ApplicationInsights": {
+ "type": "Transitive",
+ "resolved": "2.23.0",
+ "contentHash": "nWArUZTdU7iqZLycLKWe0TDms48KKGE6pONH2terYNa8REXiqixrMOkf1sk5DHGMaUTqONU2YkS4SAXBhLStgw==",
+ "dependencies": {
+ "System.Diagnostics.DiagnosticSource": "5.0.0"
+ }
+ },
+ "Microsoft.Bcl.AsyncInterfaces": {
+ "type": "Transitive",
+ "resolved": "10.0.11",
+ "contentHash": "pUkYI1f99hY9giu/MRaEVN53RO7skXwR7FGtVvFJCK5ezdg5i0W+z1yj/91X5aKv07xGuSB5n1g/Zt2P4Ooq6g==",
+ "dependencies": {
+ "System.Threading.Tasks.Extensions": "4.6.3"
+ }
+ },
+ "Microsoft.CodeCoverage": {
+ "type": "Transitive",
+ "resolved": "18.9.0",
+ "contentHash": "MtegCIKGuG0r/LCdIjoR1HgzCGIUBhR3aNdbtQpts5vMXQuqBDZ2Jsd2uFKoHFM2XrQV6ZrDf3F5oLi3SLTp8w=="
+ },
+ "Microsoft.Testing.Extensions.Telemetry": {
+ "type": "Transitive",
+ "resolved": "2.4.0",
+ "contentHash": "JeP1RFqBa11fWmBk8xEfZcMKr4rxWSyI6OZ+659V069CaMkTEOQBW2UdSSeNz3absOsygcn7JJkzerC4LGnZ9w==",
+ "dependencies": {
+ "Microsoft.ApplicationInsights": "2.23.0",
+ "Microsoft.Testing.Platform": "[2.4.0, 3.0.0)",
+ "System.Diagnostics.DiagnosticSource": "6.0.0"
+ }
+ },
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": {
+ "type": "Transitive",
+ "resolved": "2.4.0",
+ "contentHash": "uRb+4qM42dFDg4kWJZ2kFEcwESNVCRZJjItp5vETorN3rSJGysP617LqYirOcrCYmxg+obPreHMM5JcsNDKtBw==",
+ "dependencies": {
+ "Microsoft.Testing.Platform": "[2.4.0, 3.0.0)"
+ }
+ },
+ "Microsoft.Testing.Platform": {
+ "type": "Transitive",
+ "resolved": "2.4.0",
+ "contentHash": "dp1N3P1ujb0ztwFgqz2o/ItEvq+pm19/AiA+Xq7Zpcy7oPMcxDBZLYGtf0LlU1MveEBJuKVjZhI+LnkfcH/0jQ=="
+ },
+ "Microsoft.Testing.Platform.MSBuild": {
+ "type": "Transitive",
+ "resolved": "2.4.0",
+ "contentHash": "qr5M6h16YHMJLFDcWELFVMMpGte2BUmveBZKT5YoBV+bmuJRPu9bv/Zqke4yQuOEKRNxoAETrG5jr+/6Rnr3Hg==",
+ "dependencies": {
+ "Microsoft.Testing.Platform": "[2.4.0, 3.0.0)"
+ }
+ },
+ "Microsoft.TestPlatform.ObjectModel": {
+ "type": "Transitive",
+ "resolved": "18.9.0",
+ "contentHash": "Fz/qXC52VXXopzHj7v2reCKcCqSxkTDCkEDfkNAH0vni/7qK/KLMiEYtRmnUanxO2F2g2zZZ60qCpxF0AF7URA==",
+ "dependencies": {
+ "System.Collections.Immutable": "10.0.0",
+ "System.Reflection.Metadata": "8.0.0",
+ "System.ValueTuple": "4.5.0"
+ }
+ },
+ "MSTest.Analyzers": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "8g2KL2LjsXXVywvQb0uWQBANOONVnRf6cTSbON9ar6gIw08oSBY7Z3PJlC8sHbqOVvCr7srrcWiKzb12SUoV2w=="
+ },
+ "Newtonsoft.Json": {
+ "type": "Transitive",
+ "resolved": "13.0.4",
+ "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A=="
+ },
+ "RestSharp": {
+ "type": "Transitive",
+ "resolved": "114.0.0",
+ "contentHash": "8pDO4q+K8nIqXbvGCb9ciKcngj36pCXGRDzOYPEPoS2IvBIL+mejmUMAtSNALt/NkDc9LQmHvM95kdniLSbGtg==",
+ "dependencies": {
+ "System.Text.Json": "10.0.0"
+ }
+ },
+ "System.Buffers": {
+ "type": "Transitive",
+ "resolved": "4.6.1",
+ "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw=="
+ },
+ "System.Collections.Immutable": {
+ "type": "Transitive",
+ "resolved": "10.0.0",
+ "contentHash": "BHo23kBFvTFQa0tuDFXcb3Q8QInjm1Xrq+If/xuV8iMlxOOsylsa6sgRK25n5dlcMk8G2f0O/t5AdjZrdVBOXA==",
+ "dependencies": {
+ "System.Memory": "4.6.3",
+ "System.Runtime.CompilerServices.Unsafe": "6.1.2"
+ }
+ },
+ "System.Diagnostics.DiagnosticSource": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==",
+ "dependencies": {
+ "System.Memory": "4.5.4",
+ "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+ }
+ },
+ "System.IO.Pipelines": {
+ "type": "Transitive",
+ "resolved": "10.0.11",
+ "contentHash": "rg8LPOZ62quw3aTnsQNh9rssncaMs69WEM1DiJdDkV+o4Llb2s5Pasy5Bulfm8zL+pE4gDcSQo7V2zW2jvxwYg==",
+ "dependencies": {
+ "System.Buffers": "4.6.1",
+ "System.Memory": "4.6.3",
+ "System.Threading.Tasks.Extensions": "4.6.3"
+ }
+ },
+ "System.Memory": {
+ "type": "Transitive",
+ "resolved": "4.6.3",
+ "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==",
+ "dependencies": {
+ "System.Buffers": "4.6.1",
+ "System.Numerics.Vectors": "4.6.1",
+ "System.Runtime.CompilerServices.Unsafe": "6.1.2"
+ }
+ },
+ "System.Numerics.Vectors": {
+ "type": "Transitive",
+ "resolved": "4.6.1",
+ "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q=="
+ },
+ "System.Reflection.Metadata": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==",
+ "dependencies": {
+ "System.Collections.Immutable": "8.0.0",
+ "System.Memory": "4.5.5"
+ }
+ },
+ "System.Runtime.CompilerServices.Unsafe": {
+ "type": "Transitive",
+ "resolved": "6.1.2",
+ "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw=="
+ },
+ "System.Text.Encodings.Web": {
+ "type": "Transitive",
+ "resolved": "10.0.11",
+ "contentHash": "R7H2/Oqr3onFff/JKDpfRjwxcQZlocF/eQ0ce8go/OTjqz+6LEp/fZK8j1YnDobtysChATEg7krVq4hQJ3IoeQ==",
+ "dependencies": {
+ "System.Buffers": "4.6.1",
+ "System.Memory": "4.6.3",
+ "System.Runtime.CompilerServices.Unsafe": "6.1.2"
+ }
+ },
+ "System.Text.Json": {
+ "type": "Transitive",
+ "resolved": "10.0.11",
+ "contentHash": "X2rXQy7g8KEUvWVbAz6vOk6byI1eMgmzFeXbKxq4BfZCfFy5S91K+K+hd4ZBlSp0wL1Y8FyGRXs6+hvABTjwQw==",
+ "dependencies": {
+ "Microsoft.Bcl.AsyncInterfaces": "10.0.11",
+ "System.Buffers": "4.6.1",
+ "System.IO.Pipelines": "10.0.11",
+ "System.Memory": "4.6.3",
+ "System.Runtime.CompilerServices.Unsafe": "6.1.2",
+ "System.Text.Encodings.Web": "10.0.11",
+ "System.Threading.Tasks.Extensions": "4.6.3",
+ "System.ValueTuple": "4.6.2"
+ }
+ },
+ "System.Threading.Tasks.Extensions": {
+ "type": "Transitive",
+ "resolved": "4.6.3",
+ "contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==",
+ "dependencies": {
+ "System.Runtime.CompilerServices.Unsafe": "6.1.2"
+ }
+ },
+ "System.ValueTuple": {
+ "type": "Transitive",
+ "resolved": "4.6.2",
+ "contentHash": "yQgmjfFximrNm9LIV3mL6T5MzjeC+epeE5rl4hXxAlYmxby7RM1dPSkIKXk9HNkl6G54h2JHOmLD46+Pey+IRg=="
+ },
+ "MailinatorApiClient": {
+ "type": "Project",
+ "dependencies": {
+ "Newtonsoft.Json": "[13.0.4, )",
+ "RestSharp": "[114.0.0, )",
+ "System.Text.Json": "[10.0.11, )"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/mailinator-csharp-client-unit-tests/ApiClientRequestTests.cs b/mailinator-csharp-client-unit-tests/ApiClientRequestTests.cs
index 9797527..dc1f874 100644
--- a/mailinator-csharp-client-unit-tests/ApiClientRequestTests.cs
+++ b/mailinator-csharp-client-unit-tests/ApiClientRequestTests.cs
@@ -2,13 +2,21 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using mailinator_csharp_client.Clients.ApiClients.Authenticators;
using mailinator_csharp_client.Clients.ApiClients.Domains;
using mailinator_csharp_client.Clients.ApiClients.Messages;
+using mailinator_csharp_client.Clients.ApiClients.Rules;
+using mailinator_csharp_client.Clients.ApiClients.Stats;
+using mailinator_csharp_client.Clients.ApiClients.Webhooks;
using mailinator_csharp_client.Clients.HttpClient;
+using mailinator_csharp_client.Models.Authenticators.Requests;
using mailinator_csharp_client.Models.Domains.Requests;
using mailinator_csharp_client.Models.Domains.Responses;
using mailinator_csharp_client.Models.Messages.Entities;
using mailinator_csharp_client.Models.Messages.Requests;
+using mailinator_csharp_client.Models.Rules.Requests;
+using mailinator_csharp_client.Models.Webhooks.Entities;
+using mailinator_csharp_client.Models.Webhooks.Requests;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RestSharp;
@@ -17,6 +25,53 @@ namespace mailinator_csharp_client_unit_tests
[TestClass]
public class ApiClientRequestTests
{
+ [TestMethod]
+ public async Task ListDomainMessagesAsync_BuildsDomainRouteAndAllQueryParameters()
+ {
+ var httpClient = new RecordingHttpClient();
+ var client = new MessagesClient(httpClient, "domains");
+
+ await client.ListDomainMessagesAsync(new ListDomainMessagesRequest
+ {
+ Domain = "example.com", Inbox = "orders*", Skip = 10, Limit = 20,
+ Sort = Sort.asc, DecodeSubject = true, Cursor = "next+page=",
+ Full = true, Delete = "30s", Wait = "10s"
+ });
+
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/inboxes",
+ "domain", "example.com", "inbox", "orders*", "skip", "10", "limit", "20",
+ "sort", "asc", "decode_subject", "True", "cursor", "next+page=",
+ "full", "True", "delete", "30s", "wait", "10s");
+ Assert.AreEqual(9, httpClient.Request.Parameters.Count(p => p.Type == ParameterType.QueryString));
+ Assert.AreEqual(ParameterType.QueryString, httpClient.Request.Parameters.Single(p => p.Name == "inbox").Type);
+ Assert.AreEqual(ParameterType.UrlSegment, httpClient.Request.Parameters.Single(p => p.Name == "domain").Type);
+ }
+
+ [TestMethod]
+ public async Task ListDomainMessagesAsync_DefaultsToEntireDomainWithoutOptionalFilters()
+ {
+ var httpClient = new RecordingHttpClient();
+ var client = new MessagesClient(httpClient, "domains");
+
+ await client.ListDomainMessagesAsync(new ListDomainMessagesRequest());
+
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/inboxes",
+ "domain", "private", "skip", "0", "limit", "50", "sort", "desc", "decode_subject", "False");
+ CollectionAssert.AreEquivalent(new[] { "skip", "limit", "sort", "decode_subject" },
+ httpClient.Request.Parameters.Where(p => p.Type == ParameterType.QueryString).Select(p => p.Name).ToArray());
+ }
+
+ [TestMethod]
+ public async Task ListDomainMessagesAsync_PreservesWildcardAndExplicitFalse()
+ {
+ var httpClient = new RecordingHttpClient();
+ var client = new MessagesClient(httpClient, "domains");
+
+ await client.ListDomainMessagesAsync(new ListDomainMessagesRequest { Inbox = "*", Full = false });
+
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/inboxes", "inbox", "*", "full", "False");
+ }
+
[TestMethod]
public async Task GetDomainAsync_BuildsGetRequestWithDomainUrlSegment()
{
@@ -65,6 +120,25 @@ public async Task FetchInboxAsync_BuildsRouteAndOptionalQueryParameters()
Assert.AreEqual("10s", ParameterValue(httpClient.Request, "wait"));
}
+ [TestMethod]
+ public async Task FetchInboxMessageAsync_ForwardsDeleteQueryParameter()
+ {
+ var httpClient = new RecordingHttpClient();
+ var client = new MessagesClient(httpClient, "domains");
+
+ await client.FetchInboxMessageAsync(new FetchInboxMessageRequest
+ {
+ Domain = "example.com",
+ Inbox = "orders",
+ MessageId = "message-123",
+ Delete = "30s"
+ });
+
+ Assert.AreEqual(Method.Get, httpClient.Request.Method);
+ Assert.AreEqual("domains/{domain}/inboxes/{inbox}/messages/{messageId}", httpClient.Request.Resource);
+ Assert.AreEqual("30s", ParameterValue(httpClient.Request, "delete"));
+ }
+
[TestMethod]
public async Task PostMessageAsync_BuildsPostRequestWithJsonBody()
{
@@ -81,11 +155,181 @@ public async Task PostMessageAsync_BuildsPostRequestWithJsonBody()
Assert.AreSame(message, httpClient.Request.Parameters.Single(parameter => parameter.Type == ParameterType.RequestBody).Value);
}
+ [TestMethod]
+ public async Task AuthenticatorsClient_BuildsRequestsForEveryOperation()
+ {
+ var httpClient = new RecordingHttpClient();
+ var client = new AuthenticatorsClient(httpClient, "");
+
+ await client.InstantTOTP2FACodeAsync(new InstantTOTP2FACodeRequest { TotpSecretKey = "secret" });
+ AssertRequest(httpClient, Method.Get, "totp/{totp_secret_key}", "totp_secret_key", "secret");
+
+#pragma warning disable CS0618
+ await client.GetAuthenticatorsAsync();
+ AssertRequest(httpClient, Method.Get, "authenticators");
+#pragma warning restore CS0618
+
+ await client.GetAuthenticatorsByIdAsync(new GetAuthenticatorsByIdRequest { Id = "auth-1" });
+ AssertRequest(httpClient, Method.Get, "authenticators/{auth_id}", "auth_id", "auth-1");
+
+#pragma warning disable CS0618
+ await client.GetAuthenticatorAsync();
+ AssertRequest(httpClient, Method.Get, "authenticator");
+ await client.GetAuthenticatorByIdAsync(new GetAuthenticatorByIdRequest { Id = "auth-1" });
+ AssertRequest(httpClient, Method.Get, "authenticator/{auth_id}", "auth_id", "auth-1");
+#pragma warning restore CS0618
+ }
+
+ [TestMethod]
+ public async Task DomainsClient_BuildsRequestsForEveryOperation()
+ {
+ var httpClient = new RecordingHttpClient();
+ var client = new DomainsClient(httpClient, "domains");
+
+ await client.GetAllDomainsAsync();
+ AssertRequest(httpClient, Method.Get, "domains/");
+
+#pragma warning disable CS0618
+ await client.CreateDomainAsync(new CreateDomainRequest { Name = "example.com" });
+ AssertRequest(httpClient, Method.Post, "domains/{domain_id}", "domain_id", "example.com");
+ await client.DeleteDomainAsync(new DeleteDomainRequest { DomainId = "example.com" });
+ AssertRequest(httpClient, Method.Delete, "domains/{domain_id}", "domain_id", "example.com");
+#pragma warning restore CS0618
+ }
+
+ [TestMethod]
+ public async Task MessagesClient_BuildsRequestsForMessageRetrievalOperations()
+ {
+ var httpClient = new RecordingHttpClient();
+ var client = new MessagesClient(httpClient, "domains");
+
+ await client.FetchMessageAsync(new FetchMessageRequest { Domain = "example.com", MessageId = "message-1", Delete = "1m" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/messages/{messageId}", "domain", "example.com", "messageId", "message-1", "delete", "1m");
+ await client.FetchSMSMessagesAsync(new FetchSMSMessagesRequest { Domain = "example.com", TeamSMSNumber = "15551234567" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/inboxes/{teamSMSNumber}", "domain", "example.com", "teamSMSNumber", "15551234567");
+ await client.FetchInboxMessageAttachmentsAsync(new FetchInboxMessageAttachmentsRequest { Domain = "example.com", Inbox = "orders", MessageId = "message-1" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/inboxes/{inbox}/messages/{messageId}/attachments", "domain", "example.com", "inbox", "orders", "messageId", "message-1");
+ await client.FetchMessageAttachmentsAsync(new FetchMessageAttachmentsRequest { Domain = "example.com", MessageId = "message-1" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/messages/{messageId}/attachments", "domain", "example.com", "messageId", "message-1");
+ await client.FetchInboxMessageAttachmentAsync(new FetchInboxMessageAttachmentRequest { Domain = "example.com", Inbox = "orders", MessageId = "message-1", AttachmentId = "file.pdf" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/inboxes/{inbox}/messages/{messageId}/attachments/{attachmentId}", "domain", "example.com", "inbox", "orders", "messageId", "message-1", "attachmentId", "file.pdf");
+ await client.FetchMessageAttachmentAsync(new FetchMessageAttachmentRequest { Domain = "example.com", MessageId = "message-1", AttachmentId = "file.pdf" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/messages/{messageId}/attachments/{attachmentId}", "domain", "example.com", "messageId", "message-1", "attachmentId", "file.pdf");
+ await client.FetchMessageLinksAsync(new FetchMessageLinksRequest { Domain = "example.com", MessageId = "message-1" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/messages/{messageId}/links", "domain", "example.com", "messageId", "message-1");
+ await client.FetchInboxMessageLinksAsync(new FetchInboxMessageLinksRequest { Domain = "example.com", Inbox = "orders", MessageId = "message-1" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/inboxes/{inbox}/messages/{messageId}/links", "domain", "example.com", "inbox", "orders", "messageId", "message-1");
+ await client.FetchMessageLinksFullAsync(new FetchMessageLinksFullRequest { Domain = "example.com", MessageId = "message-1" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/messages/{messageId}/linksfull", "domain", "example.com", "messageId", "message-1");
+ }
+
+ [TestMethod]
+ public async Task MessagesClient_BuildsRequestsForDeletionAndContentOperations()
+ {
+ var httpClient = new RecordingHttpClient();
+ var client = new MessagesClient(httpClient, "domains");
+
+ await client.DeleteAllDomainMessagesAsync(new DeleteAllDomainMessagesRequest { Domain = "example.com" });
+ AssertRequest(httpClient, Method.Delete, "domains/{domain}/inboxes", "domain", "example.com");
+ await client.DeleteAllInboxMessagesAsync(new DeleteAllInboxMessagesRequest { Domain = "example.com", Inbox = "orders" });
+ AssertRequest(httpClient, Method.Delete, "domains/{domain}/inboxes/{inbox}", "domain", "example.com", "inbox", "orders");
+ await client.DeleteMessageAsync(new DeleteMessageRequest { Domain = "example.com", Inbox = "orders", MessageId = "message-1" });
+ AssertRequest(httpClient, Method.Delete, "domains/{domain}/inboxes/{inbox}/messages/{messageId}", "domain", "example.com", "inbox", "orders", "messageId", "message-1");
+ await client.FetchMessageSmtpLogAsync(new FetchMessageSmtpLogRequest { Domain = "example.com", MessageId = "message-1" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/messages/{messageId}/smtplog", "domain", "example.com", "messageId", "message-1");
+ await client.FetchInboxMessageSmtpLogAsync(new FetchInboxMessageSmtpLogRequest { Domain = "example.com", Inbox = "orders", MessageId = "message-1" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/inboxes/{inbox}/messages/{messageId}/smtplog", "domain", "example.com", "inbox", "orders", "messageId", "message-1");
+ await client.FetchMessageRawAsync(new FetchMessageRawRequest { Domain = "example.com", MessageId = "message-1" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/messages/{messageId}/raw", "domain", "example.com", "messageId", "message-1");
+ await client.FetchInboxMessageRawAsync(new FetchInboxMessageRawRequest { Domain = "example.com", Inbox = "orders", MessageId = "message-1" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/inboxes/{inbox}/messages/{messageId}/raw", "domain", "example.com", "inbox", "orders", "messageId", "message-1");
+
+#pragma warning disable CS0618
+ await client.FetchLatestMessagesAsync(new FetchLatestMessagesRequest { Domain = "example.com" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/messages/*", "domain", "example.com");
+ await client.FetchLatestInboxMessagesAsync(new FetchLatestInboxMessagesRequest { Domain = "example.com", Inbox = "orders" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain}/inboxes/{inbox}/messages/*", "domain", "example.com", "inbox", "orders");
+#pragma warning restore CS0618
+ }
+
+ [TestMethod]
+ public async Task RulesClient_BuildsRequestsForEveryOperation()
+ {
+ var httpClient = new RecordingHttpClient();
+ var client = new RulesClient(httpClient, "domains");
+ var createRequest = new CreateRuleRequest { DomainId = "example.com" };
+
+#pragma warning disable CS0618
+ await client.CreateRuleAsync(createRequest);
+ AssertRequest(httpClient, Method.Post, "domains/{domain_id}/rules", "domain_id", "example.com");
+ Assert.AreSame(createRequest.Rule, BodyValue(httpClient.Request));
+ await client.EnableRuleAsync(new EnableRuleRequest { DomainId = "example.com", RuleId = "rule-1" });
+ AssertRequest(httpClient, Method.Put, "domains/{domain_id}/rules/{ruleId}/enable", "domain_id", "example.com", "ruleId", "rule-1");
+ await client.DisableRuleAsync(new DisableRuleRequest { DomainId = "example.com", RuleId = "rule-1" });
+ AssertRequest(httpClient, Method.Put, "domains/{domain_id}/rules/{ruleId}/disable", "domain_id", "example.com", "ruleId", "rule-1");
+ await client.GetAllRulesAsync(new GetAllRulesRequest { DomainId = "example.com" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain_id}/rules", "domain_id", "example.com");
+ await client.GetRuleAsync(new GetRuleRequest { DomainId = "example.com", RuleId = "rule-1" });
+ AssertRequest(httpClient, Method.Get, "domains/{domain_id}/rules/{ruleId}", "domain_id", "example.com", "ruleId", "rule-1");
+ await client.DeleteRuleAsync(new DeleteRuleRequest { DomainId = "example.com", RuleId = "rule-1" });
+ AssertRequest(httpClient, Method.Delete, "domains/{domain_id}/rules/{ruleId}", "domain_id", "example.com", "ruleId", "rule-1");
+#pragma warning restore CS0618
+ }
+
+ [TestMethod]
+ public async Task StatsClient_BuildsRequestsForEveryOperation()
+ {
+ var httpClient = new RecordingHttpClient();
+ var client = new StatsClient(httpClient, "stats");
+
+ await client.GetTeamStatsAsync();
+ AssertRequest(httpClient, Method.Get, "statsteam/stats");
+ await client.GetTeamAsync();
+ AssertRequest(httpClient, Method.Get, "stats/team");
+ await client.GetTeamInfoAsync();
+ AssertRequest(httpClient, Method.Get, "stats/teaminfo");
+ }
+
+ [TestMethod]
+ public async Task WebhooksClient_BuildsRequestsForEveryOperation()
+ {
+ var httpClient = new RecordingHttpClient();
+ var client = new WebhooksClient(httpClient, "webhooks");
+ var webhook = new Webhook { From = "sender@example.com", Subject = "Hello" };
+
+ await client.PrivateWebhookAsync(new PrivateWebhookRequest { WebhookToken = "token", Webhook = webhook });
+ AssertRequest(httpClient, Method.Post, "webhooks/private/webhook", "whtoken", "token");
+ Assert.AreSame(webhook, BodyValue(httpClient.Request));
+ await client.PrivateInboxWebhookAsync(new PrivateInboxWebhookRequest { WebhookToken = "token", Inbox = "orders", Webhook = webhook });
+ AssertRequest(httpClient, Method.Post, "webhooks/private/webhook/{inbox}", "whtoken", "token", "inbox", "orders");
+ await client.PrivateCustomServiceWebhookAsync(new PrivateCustomServiceWebhookRequest { WebhookToken = "token", CustomService = "twilio", Webhook = webhook });
+ AssertRequest(httpClient, Method.Post, "webhooks/private/{customService}", "whtoken", "token", "customService", "twilio");
+ await client.PrivateCustomServiceInboxWebhookAsync(new PrivateCustomServiceInboxWebhookRequest { WebhookToken = "token", CustomService = "twilio", Inbox = "orders", Webhook = webhook });
+ AssertRequest(httpClient, Method.Post, "webhooks/private/{customService}/{inbox}", "whtoken", "token", "customService", "twilio", "inbox", "orders");
+ }
+
private static object ParameterValue(RestRequest request, string name)
{
return request.Parameters.Single(parameter => parameter.Name == name).Value;
}
+ private static object BodyValue(RestRequest request)
+ {
+ return request.Parameters.Single(parameter => parameter.Type == ParameterType.RequestBody).Value;
+ }
+
+ private static void AssertRequest(RecordingHttpClient httpClient, Method method, string resource, params string[] parameters)
+ {
+ Assert.AreEqual(method, httpClient.Request.Method);
+ Assert.AreEqual(resource, httpClient.Request.Resource);
+ Assert.AreEqual(0, parameters.Length % 2, "Expected parameter names and values in pairs.");
+
+ for (var index = 0; index < parameters.Length; index += 2)
+ {
+ Assert.AreEqual(parameters[index + 1], ParameterValue(httpClient.Request, parameters[index])?.ToString());
+ }
+ }
+
private sealed class RecordingHttpClient : IHttpClient
{
public RestRequest Request { get; private set; }
diff --git a/mailinator-csharp-client-unit-tests/MessageContentTests.cs b/mailinator-csharp-client-unit-tests/MessageContentTests.cs
new file mode 100644
index 0000000..c3e908e
--- /dev/null
+++ b/mailinator-csharp-client-unit-tests/MessageContentTests.cs
@@ -0,0 +1,129 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using mailinator_csharp_client.Clients.ApiClients.Messages;
+using mailinator_csharp_client.Clients.HttpClient;
+using mailinator_csharp_client.Models.Messages.Requests;
+using mailinator_csharp_client.Models.Responses;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using RestSharp;
+
+namespace mailinator_csharp_client_unit_tests
+{
+ [TestClass]
+ public class MessageContentTests
+ {
+ [TestMethod]
+ public async Task GetMessageTextAsync_BuildsGetRequestAndReturnsResponse()
+ {
+ var expected = new GetMessageTextResponse { Text = "content" };
+ var http = new ContentHttpClient(expected);
+ var client = new MessagesClient(http, "domains");
+
+ var response = await client.GetMessageTextAsync(new GetMessageTextRequest
+ {
+ Domain = "example.com", MessageId = "message+123/part"
+ });
+
+ Assert.AreEqual(Method.Get, http.Request.Method);
+ Assert.AreEqual("domains/{domain}/messages/{messageId}/text", http.Request.Resource);
+ Assert.AreEqual(2, http.Request.Parameters.Count());
+ Assert.IsTrue(http.Request.Parameters.All(p => p.Type == ParameterType.UrlSegment));
+ Assert.AreEqual("example.com", http.Request.Parameters.Single(p => p.Name == "domain").Value);
+ Assert.AreEqual("message+123/part", http.Request.Parameters.Single(p => p.Name == "messageId").Value);
+ Assert.AreSame(expected, response);
+ }
+
+ [TestMethod]
+ [DataRow("")]
+ [DataRow("Hello\r\nWorld =C2=A0 café HTML & text
")]
+ public void GetMessageTextResponse_DeserializesContentWithoutTransformingIt(string content)
+ {
+ var json = new JObject { ["text"] = content }.ToString();
+ var response = JsonConvert.DeserializeObject(json);
+
+ Assert.AreEqual(content, response.Text);
+ }
+
+ [TestMethod]
+ public async Task GetMessageTextPlainAsync_BuildsGetRequestAndReturnsResponse()
+ {
+ var expected = new GetMessageTextPlainResponse { TextPlain = "content" };
+ var http = new ContentHttpClient(expected);
+ var client = new MessagesClient(http, "domains");
+
+ var response = await client.GetMessageTextPlainAsync(new GetMessageTextPlainRequest
+ {
+ Domain = "example.com", MessageId = "message+123/part"
+ });
+
+ Assert.AreEqual(Method.Get, http.Request.Method);
+ Assert.AreEqual("domains/{domain}/messages/{messageId}/textplain", http.Request.Resource);
+ Assert.AreEqual(2, http.Request.Parameters.Count());
+ Assert.IsTrue(http.Request.Parameters.All(p => p.Type == ParameterType.UrlSegment));
+ Assert.AreEqual("example.com", http.Request.Parameters.Single(p => p.Name == "domain").Value);
+ Assert.AreEqual("message+123/part", http.Request.Parameters.Single(p => p.Name == "messageId").Value);
+ Assert.AreSame(expected, response);
+ }
+
+ [TestMethod]
+ [DataRow("")]
+ [DataRow("Hello\r\nWorld =C2=A0 café HTML & text
")]
+ public void GetMessageTextPlainResponse_DeserializesContentWithoutTransformingIt(string content)
+ {
+ var json = new JObject { ["text/plain"] = content }.ToString();
+ var response = JsonConvert.DeserializeObject(json);
+
+ Assert.AreEqual(content, response.TextPlain);
+ }
+
+ [TestMethod]
+ public async Task GetMessageTextHtmlAsync_BuildsGetRequestAndReturnsResponse()
+ {
+ var expected = new GetMessageTextHtmlResponse { TextHtml = "content" };
+ var http = new ContentHttpClient(expected);
+ var client = new MessagesClient(http, "domains");
+
+ var response = await client.GetMessageTextHtmlAsync(new GetMessageTextHtmlRequest
+ {
+ Domain = "example.com", MessageId = "message+123/part"
+ });
+
+ Assert.AreEqual(Method.Get, http.Request.Method);
+ Assert.AreEqual("domains/{domain}/messages/{messageId}/texthtml", http.Request.Resource);
+ Assert.AreEqual(2, http.Request.Parameters.Count());
+ Assert.IsTrue(http.Request.Parameters.All(p => p.Type == ParameterType.UrlSegment));
+ Assert.AreEqual("example.com", http.Request.Parameters.Single(p => p.Name == "domain").Value);
+ Assert.AreEqual("message+123/part", http.Request.Parameters.Single(p => p.Name == "messageId").Value);
+ Assert.AreSame(expected, response);
+ }
+
+ [TestMethod]
+ [DataRow("")]
+ [DataRow("Hello\r\nWorld =C2=A0 café HTML & text
")]
+ public void GetMessageTextHtmlResponse_DeserializesContentWithoutTransformingIt(string content)
+ {
+ var json = new JObject { ["text/html"] = content }.ToString();
+ var response = JsonConvert.DeserializeObject(json);
+
+ Assert.AreEqual(content, response.TextHtml);
+ }
+
+ private sealed class ContentHttpClient : IHttpClient
+ {
+ private readonly object response;
+ public RestRequest Request { get; private set; }
+ public ContentHttpClient(object response) => this.response = response;
+ public RestRequest GetRequest(string url, Method method) => new RestRequest(url, method);
+ public Task ExecuteAsync(RestRequest request)
+ {
+ Request = request;
+ return Task.FromResult((T)response);
+ }
+ public Task ExecuteAsync(RestRequest request, Func customDeserializationFunction)
+ => throw new NotSupportedException();
+ }
+ }
+}
diff --git a/mailinator-csharp-client-unit-tests/MessageHeadersTests.cs b/mailinator-csharp-client-unit-tests/MessageHeadersTests.cs
new file mode 100644
index 0000000..a7e4834
--- /dev/null
+++ b/mailinator-csharp-client-unit-tests/MessageHeadersTests.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using mailinator_csharp_client.Clients.ApiClients.Messages;
+using mailinator_csharp_client.Clients.HttpClient;
+using mailinator_csharp_client.Models.Messages.Requests;
+using mailinator_csharp_client.Models.Responses;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using RestSharp;
+
+namespace mailinator_csharp_client_unit_tests
+{
+ [TestClass]
+ public class MessageHeadersTests
+ {
+ [TestMethod]
+ public async Task GetMessageHeadersAsync_BuildsGetRequestAndReturnsResponse()
+ {
+ var http = new HeadersHttpClient();
+ var client = new MessagesClient(http, "domains");
+
+ var response = await client.GetMessageHeadersAsync(new GetMessageHeadersRequest
+ {
+ Domain = "example.com", MessageId = "message+123"
+ });
+
+ Assert.AreEqual(Method.Get, http.Request.Method);
+ Assert.AreEqual("domains/{domain}/messages/{messageId}/headers", http.Request.Resource);
+ Assert.AreEqual(2, http.Request.Parameters.Count());
+ Assert.IsTrue(http.Request.Parameters.All(p => p.Type == ParameterType.UrlSegment));
+ Assert.AreEqual("example.com", http.Request.Parameters.Single(p => p.Name == "domain").Value);
+ Assert.AreEqual("message+123", http.Request.Parameters.Single(p => p.Name == "messageId").Value);
+ Assert.AreSame(http.Response, response);
+ }
+
+ [TestMethod]
+ public void GetMessageHeadersResponse_DeserializesStringsArraysAndCustomHeaders()
+ {
+ var response = JsonConvert.DeserializeObject(
+ "{\"headers\":{\"subject\":\"Test\",\"received\":[\"hop one\",\"hop two\"],\"x-custom\":\"custom value\"}}");
+
+ Assert.AreEqual("Test", response.Headers["subject"]);
+ CollectionAssert.AreEqual(new[] { "hop one", "hop two" }, ((JArray)response.Headers["received"]).ToObject());
+ Assert.AreEqual("custom value", response.Headers["x-custom"]);
+ }
+
+ [TestMethod]
+ public void GetMessageHeadersResponse_DeserializesEmptyHeaders()
+ {
+ var response = JsonConvert.DeserializeObject("{\"headers\":{}}");
+ Assert.IsNotNull(response.Headers);
+ Assert.AreEqual(0, response.Headers.Count);
+ }
+
+ private sealed class HeadersHttpClient : IHttpClient
+ {
+ public RestRequest Request { get; private set; }
+ public GetMessageHeadersResponse Response { get; } = new GetMessageHeadersResponse();
+ public RestRequest GetRequest(string url, Method method) => new RestRequest(url, method);
+ public Task ExecuteAsync(RestRequest request)
+ {
+ Request = request;
+ return Task.FromResult((T)(object)Response);
+ }
+ public Task ExecuteAsync(RestRequest request, Func customDeserializationFunction)
+ => throw new NotSupportedException();
+ }
+ }
+}
diff --git a/mailinator-csharp-client-unit-tests/MessageSummaryTests.cs b/mailinator-csharp-client-unit-tests/MessageSummaryTests.cs
new file mode 100644
index 0000000..7c391e8
--- /dev/null
+++ b/mailinator-csharp-client-unit-tests/MessageSummaryTests.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using mailinator_csharp_client.Clients.ApiClients.Messages;
+using mailinator_csharp_client.Clients.HttpClient;
+using mailinator_csharp_client.Models.Messages.Requests;
+using mailinator_csharp_client.Models.Responses;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Newtonsoft.Json;
+using RestSharp;
+
+namespace mailinator_csharp_client_unit_tests
+{
+ [TestClass]
+ public class MessageSummaryTests
+ {
+ [TestMethod]
+ public async Task GetMessageSummaryAsync_BuildsGetRequestAndReturnsResponse()
+ {
+ var http = new SummaryHttpClient();
+ var client = new MessagesClient(http, "domains");
+
+ var response = await client.GetMessageSummaryAsync(new GetMessageSummaryRequest
+ {
+ Domain = "example.com", MessageId = "message+123"
+ });
+
+ Assert.AreEqual(Method.Get, http.Request.Method);
+ Assert.AreEqual("domains/{domain}/messages/{messageId}/summary", http.Request.Resource);
+ Assert.AreEqual(2, http.Request.Parameters.Count());
+ Assert.IsTrue(http.Request.Parameters.All(p => p.Type == ParameterType.UrlSegment));
+ Assert.AreEqual("example.com", http.Request.Parameters.Single(p => p.Name == "domain").Value);
+ Assert.AreEqual("message+123", http.Request.Parameters.Single(p => p.Name == "messageId").Value);
+ Assert.AreSame(http.Response, response);
+ }
+
+ [TestMethod]
+ public void GetMessageSummaryResponse_DeserializesWrappedMetadataAnd64BitTime()
+ {
+ var response = JsonConvert.DeserializeObject(
+ "{\"summary\":{\"subject\":\"Test\",\"domain\":\"example.com\",\"from\":\"sender@example.com\",\"id\":\"message-123\",\"to\":\"orders\",\"time\":1788909009000}}");
+
+ Assert.IsNotNull(response.Summary);
+ Assert.AreEqual("Test", response.Summary.Subject);
+ Assert.AreEqual("example.com", response.Summary.Domain);
+ Assert.AreEqual("sender@example.com", response.Summary.From);
+ Assert.AreEqual("message-123", response.Summary.Id);
+ Assert.AreEqual("orders", response.Summary.To);
+ Assert.AreEqual(1788909009000L, response.Summary.Time);
+ Assert.IsNull(response.Summary.Parts);
+ Assert.IsNull(response.Summary.Text);
+ }
+ private sealed class SummaryHttpClient : IHttpClient
+ {
+ public RestRequest Request { get; private set; }
+ public GetMessageSummaryResponse Response { get; } = new GetMessageSummaryResponse();
+ public RestRequest GetRequest(string url, Method method) => new RestRequest(url, method);
+ public Task ExecuteAsync(RestRequest request)
+ {
+ Request = request;
+ return Task.FromResult((T)(object)Response);
+ }
+ public Task ExecuteAsync(RestRequest request, Func customDeserializationFunction)
+ => throw new NotSupportedException();
+ }
+ }
+}
+
diff --git a/mailinator-csharp-client-unit-tests/WebhookMessageTests.cs b/mailinator-csharp-client-unit-tests/WebhookMessageTests.cs
new file mode 100644
index 0000000..302ea58
--- /dev/null
+++ b/mailinator-csharp-client-unit-tests/WebhookMessageTests.cs
@@ -0,0 +1,112 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using mailinator_csharp_client.Clients.ApiClients.Webhooks;
+using mailinator_csharp_client.Clients.HttpClient;
+using mailinator_csharp_client.Models.Webhooks.Entities;
+using mailinator_csharp_client.Models.Webhooks.Requests;
+using mailinator_csharp_client.Models.Webhooks.Responses;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using RestSharp;
+
+namespace mailinator_csharp_client_unit_tests
+{
+ [TestClass]
+ public class WebhookMessageTests
+ {
+ [TestMethod]
+ [DataRow("example.com", "test-token+123")]
+ [DataRow("test-token+123", null)]
+ public async Task PostWebhookMessageAsync_BuildsRequestForBothAuthenticationForms(string domain, string token)
+ {
+ var http = new RecordingHttpClient();
+ var client = new WebhooksClient(http, "domains");
+ var body = new WebhookMessage { To = "orders", Text = "Hello" };
+ var response = await client.PostWebhookMessageAsync(new PostWebhookMessageRequest
+ {
+ Domain = domain, WebhookToken = token, Webhook = body
+ });
+ Assert.AreEqual(Method.Post, http.Request.Method);
+ Assert.AreEqual("domains/{domain}/webhook", http.Request.Resource);
+ Assert.AreEqual(domain, http.Request.Parameters.Single(p => p.Name == "domain" && p.Type == ParameterType.UrlSegment).Value);
+
+ var query = http.Request.Parameters.Where(p => p.Type == ParameterType.QueryString).ToArray();
+ Assert.AreEqual(token == null ? 0 : 1, query.Length);
+ if (token != null)
+ {
+ Assert.AreEqual("whtoken", query[0].Name);
+ Assert.AreEqual(token, query[0].Value);
+ }
+ Assert.AreSame(body, http.Request.Parameters.Single(p => p.Type == ParameterType.RequestBody).Value);
+ Assert.IsFalse(http.Request.Parameters.Any(p => p.Name == "token"));
+ Assert.AreSame(http.Response, response);
+ }
+
+ [TestMethod]
+ [DataRow("example.com", "test-token+123")]
+ [DataRow("test-token+123", null)]
+ public async Task PostWebhookInboxMessageAsync_BuildsRequestForBothAuthenticationForms(string domain, string token)
+ {
+ var http = new RecordingHttpClient();
+ var client = new WebhooksClient(http, "domains");
+ var body = new WebhookMessage { To = "orders", Text = "Hello" };
+ var response = await client.PostWebhookInboxMessageAsync(new PostWebhookInboxMessageRequest
+ {
+ Domain = domain, WebhookToken = token, Webhook = body, Inbox = "orders+test"
+ });
+ Assert.AreEqual(Method.Post, http.Request.Method);
+ Assert.AreEqual("domains/{domain}/webhook/{inbox}", http.Request.Resource);
+ Assert.AreEqual(domain, http.Request.Parameters.Single(p => p.Name == "domain" && p.Type == ParameterType.UrlSegment).Value);
+ Assert.AreEqual("orders+test", http.Request.Parameters.Single(p => p.Name == "inbox" && p.Type == ParameterType.UrlSegment).Value);
+ var query = http.Request.Parameters.Where(p => p.Type == ParameterType.QueryString).ToArray();
+ Assert.AreEqual(token == null ? 0 : 1, query.Length);
+ if (token != null)
+ {
+ Assert.AreEqual("whtoken", query[0].Name);
+ Assert.AreEqual(token, query[0].Value);
+ }
+ Assert.AreSame(body, http.Request.Parameters.Single(p => p.Type == ParameterType.RequestBody).Value);
+ Assert.IsFalse(http.Request.Parameters.Any(p => p.Name == "token"));
+ Assert.AreSame(http.Response, response);
+ }
+
+ [TestMethod]
+ public void WebhookMessage_PreservesDocumentedAndCustomPayloadFields()
+ {
+ var body = JsonConvert.DeserializeObject(
+ "{\"from\":\"sender\",\"to\":\"orders\",\"subject\":\"Test\",\"text\":\"plain\",\"html\":\"HTML
\",\"headers\":{\"X-Test\":\"value\"},\"custom\":{\"count\":2}}");
+ Assert.AreEqual("HTML
", body.Html);
+ Assert.AreEqual("value", body.Headers["X-Test"]);
+ var json = JObject.Parse(JsonConvert.SerializeObject(body));
+ Assert.AreEqual("sender", (string)json["from"]);
+ Assert.AreEqual("orders", (string)json["to"]);
+ Assert.AreEqual("Test", (string)json["subject"]);
+ Assert.AreEqual("plain", (string)json["text"]);
+ Assert.AreEqual(2, (int)json["custom"]["count"]);
+ }
+
+ [TestMethod]
+ public void PostWebhookMessageResponse_DeserializesStatusAndId()
+ {
+ var response = JsonConvert.DeserializeObject("{\"status\":\"ok\",\"id\":\"message-123\"}");
+ Assert.AreEqual("ok", response.Status);
+ Assert.AreEqual("message-123", response.Id);
+ }
+
+ private sealed class RecordingHttpClient : IHttpClient
+ {
+ public RestRequest Request { get; private set; }
+ public PostWebhookMessageResponse Response { get; } = new PostWebhookMessageResponse();
+ public RestRequest GetRequest(string url, Method method) => new RestRequest(url, method);
+ public Task ExecuteAsync(RestRequest request)
+ {
+ Request = request;
+ return Task.FromResult((T)(object)Response);
+ }
+ public Task ExecuteAsync(RestRequest request, Func customDeserializationFunction)
+ => throw new NotSupportedException();
+ }
+ }
+}
diff --git a/mailinator-csharp-client-unit-tests/mailinator-csharp-client-unit-tests.csproj b/mailinator-csharp-client-unit-tests/mailinator-csharp-client-unit-tests.csproj
index 2b84370..246d6b2 100644
--- a/mailinator-csharp-client-unit-tests/mailinator-csharp-client-unit-tests.csproj
+++ b/mailinator-csharp-client-unit-tests/mailinator-csharp-client-unit-tests.csproj
@@ -5,9 +5,9 @@
true
-
-
-
+
+
+
diff --git a/mailinator-csharp-client-unit-tests/packages.lock.json b/mailinator-csharp-client-unit-tests/packages.lock.json
new file mode 100644
index 0000000..9b3780c
--- /dev/null
+++ b/mailinator-csharp-client-unit-tests/packages.lock.json
@@ -0,0 +1,142 @@
+{
+ "version": 1,
+ "dependencies": {
+ "net8.0": {
+ "Microsoft.NET.Test.Sdk": {
+ "type": "Direct",
+ "requested": "[18.9.0, )",
+ "resolved": "18.9.0",
+ "contentHash": "xIzVXa/VpKXqDWzyC/5Hw8JbFY5u9k3Jc53CpcjBiOXvkQGio484LD9FNwOiGUSMDcYL3Rcw9sNgGi5WrswlPQ==",
+ "dependencies": {
+ "Microsoft.CodeCoverage": "18.9.0",
+ "Microsoft.TestPlatform.TestHost": "18.9.0"
+ }
+ },
+ "MSTest.TestAdapter": {
+ "type": "Direct",
+ "requested": "[4.4.0, )",
+ "resolved": "4.4.0",
+ "contentHash": "xZdlCr2r/6XFOo67jcemJsfm46tTgXdqtnISFIuDUAJf5Yxk07XpTC9eMUErLdlYD8QM9zw07eOPre7+nTayUg==",
+ "dependencies": {
+ "MSTest.TestFramework": "4.4.0",
+ "Microsoft.TestPlatform.ObjectModel": "18.9.0",
+ "Microsoft.Testing.Extensions.Telemetry": "2.4.0",
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.4.0",
+ "Microsoft.Testing.Platform.MSBuild": "2.4.0"
+ }
+ },
+ "MSTest.TestFramework": {
+ "type": "Direct",
+ "requested": "[4.4.0, )",
+ "resolved": "4.4.0",
+ "contentHash": "sfJzj3ntdU/GahJd2n+WQLgKqbV0RRMJVXtjRwRQLhblevlzLSDAOocOKANhfMaCEQ5dMt6348ztbVq/Ap60xA==",
+ "dependencies": {
+ "MSTest.Analyzers": "4.4.0"
+ }
+ },
+ "Microsoft.ApplicationInsights": {
+ "type": "Transitive",
+ "resolved": "2.23.0",
+ "contentHash": "nWArUZTdU7iqZLycLKWe0TDms48KKGE6pONH2terYNa8REXiqixrMOkf1sk5DHGMaUTqONU2YkS4SAXBhLStgw==",
+ "dependencies": {
+ "System.Diagnostics.DiagnosticSource": "5.0.0"
+ }
+ },
+ "Microsoft.CodeCoverage": {
+ "type": "Transitive",
+ "resolved": "18.9.0",
+ "contentHash": "MtegCIKGuG0r/LCdIjoR1HgzCGIUBhR3aNdbtQpts5vMXQuqBDZ2Jsd2uFKoHFM2XrQV6ZrDf3F5oLi3SLTp8w=="
+ },
+ "Microsoft.Testing.Extensions.Telemetry": {
+ "type": "Transitive",
+ "resolved": "2.4.0",
+ "contentHash": "JeP1RFqBa11fWmBk8xEfZcMKr4rxWSyI6OZ+659V069CaMkTEOQBW2UdSSeNz3absOsygcn7JJkzerC4LGnZ9w==",
+ "dependencies": {
+ "Microsoft.ApplicationInsights": "2.23.0",
+ "Microsoft.Testing.Platform": "[2.4.0, 3.0.0)"
+ }
+ },
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": {
+ "type": "Transitive",
+ "resolved": "2.4.0",
+ "contentHash": "uRb+4qM42dFDg4kWJZ2kFEcwESNVCRZJjItp5vETorN3rSJGysP617LqYirOcrCYmxg+obPreHMM5JcsNDKtBw==",
+ "dependencies": {
+ "Microsoft.Testing.Platform": "[2.4.0, 3.0.0)"
+ }
+ },
+ "Microsoft.Testing.Platform": {
+ "type": "Transitive",
+ "resolved": "2.4.0",
+ "contentHash": "dp1N3P1ujb0ztwFgqz2o/ItEvq+pm19/AiA+Xq7Zpcy7oPMcxDBZLYGtf0LlU1MveEBJuKVjZhI+LnkfcH/0jQ=="
+ },
+ "Microsoft.Testing.Platform.MSBuild": {
+ "type": "Transitive",
+ "resolved": "2.4.0",
+ "contentHash": "qr5M6h16YHMJLFDcWELFVMMpGte2BUmveBZKT5YoBV+bmuJRPu9bv/Zqke4yQuOEKRNxoAETrG5jr+/6Rnr3Hg==",
+ "dependencies": {
+ "Microsoft.Testing.Platform": "[2.4.0, 3.0.0)"
+ }
+ },
+ "Microsoft.TestPlatform.ObjectModel": {
+ "type": "Transitive",
+ "resolved": "18.9.0",
+ "contentHash": "Fz/qXC52VXXopzHj7v2reCKcCqSxkTDCkEDfkNAH0vni/7qK/KLMiEYtRmnUanxO2F2g2zZZ60qCpxF0AF7URA=="
+ },
+ "Microsoft.TestPlatform.TestHost": {
+ "type": "Transitive",
+ "resolved": "18.9.0",
+ "contentHash": "Oq+Pma/J86aZL72t71bcESvDszDsTjUfl6PIsuDdPoKTHZfNMhKamCfLD5fKx51W/u0KozXtJo2/3fnt0sgPxg==",
+ "dependencies": {
+ "Microsoft.TestPlatform.ObjectModel": "18.9.0"
+ }
+ },
+ "MSTest.Analyzers": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "8g2KL2LjsXXVywvQb0uWQBANOONVnRf6cTSbON9ar6gIw08oSBY7Z3PJlC8sHbqOVvCr7srrcWiKzb12SUoV2w=="
+ },
+ "Newtonsoft.Json": {
+ "type": "Transitive",
+ "resolved": "13.0.4",
+ "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A=="
+ },
+ "RestSharp": {
+ "type": "Transitive",
+ "resolved": "114.0.0",
+ "contentHash": "8pDO4q+K8nIqXbvGCb9ciKcngj36pCXGRDzOYPEPoS2IvBIL+mejmUMAtSNALt/NkDc9LQmHvM95kdniLSbGtg=="
+ },
+ "System.Diagnostics.DiagnosticSource": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA=="
+ },
+ "System.IO.Pipelines": {
+ "type": "Transitive",
+ "resolved": "10.0.11",
+ "contentHash": "rg8LPOZ62quw3aTnsQNh9rssncaMs69WEM1DiJdDkV+o4Llb2s5Pasy5Bulfm8zL+pE4gDcSQo7V2zW2jvxwYg=="
+ },
+ "System.Text.Encodings.Web": {
+ "type": "Transitive",
+ "resolved": "10.0.11",
+ "contentHash": "R7H2/Oqr3onFff/JKDpfRjwxcQZlocF/eQ0ce8go/OTjqz+6LEp/fZK8j1YnDobtysChATEg7krVq4hQJ3IoeQ=="
+ },
+ "System.Text.Json": {
+ "type": "Transitive",
+ "resolved": "10.0.11",
+ "contentHash": "X2rXQy7g8KEUvWVbAz6vOk6byI1eMgmzFeXbKxq4BfZCfFy5S91K+K+hd4ZBlSp0wL1Y8FyGRXs6+hvABTjwQw==",
+ "dependencies": {
+ "System.IO.Pipelines": "10.0.11",
+ "System.Text.Encodings.Web": "10.0.11"
+ }
+ },
+ "MailinatorApiClient": {
+ "type": "Project",
+ "dependencies": {
+ "Newtonsoft.Json": "[13.0.4, )",
+ "RestSharp": "[114.0.0, )",
+ "System.Text.Json": "[10.0.11, )"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/mailinator-csharp-client/Clients/ApiClients/Authenticators/AuthenticatorsClient.cs b/mailinator-csharp-client/Clients/ApiClients/Authenticators/AuthenticatorsClient.cs
index b12427e..e991949 100644
--- a/mailinator-csharp-client/Clients/ApiClients/Authenticators/AuthenticatorsClient.cs
+++ b/mailinator-csharp-client/Clients/ApiClients/Authenticators/AuthenticatorsClient.cs
@@ -41,6 +41,7 @@ public async Task InstantTOTP2FACodeAsync(InstantTOT
/// Fetch Authenticators
///
///
+ [System.Obsolete("Deprecated: This authenticator list endpoint is not present in the current Mailinator OpenAPI spec. Use GetAuthenticatorsByIdAsync for the documented stored-authenticator operation. This method may be removed in a future major release.")]
public async Task GetAuthenticatorsAsync()
{
var requestObject = httpClient.GetRequest(endpointUrl + "authenticators", Method.Get);
@@ -69,6 +70,7 @@ public async Task GetAuthenticatorsByIdAsync(GetA
/// Fetch Authenticator
///
///
+ [System.Obsolete("Deprecated: This authenticator endpoint is not present in the current Mailinator OpenAPI spec. Use GetAuthenticatorsByIdAsync for the documented stored-authenticator operation. This method may be removed in a future major release.")]
public async Task GetAuthenticatorAsync()
{
var requestObject = httpClient.GetRequest(endpointUrl + "authenticator", Method.Get);
@@ -82,6 +84,7 @@ public async Task GetAuthenticatorAsync()
///
/// GetAuthenticatorByIdRequest object.
///
+ [System.Obsolete("Deprecated: This authenticator endpoint is not present in the current Mailinator OpenAPI spec. Use GetAuthenticatorsByIdAsync for the documented stored-authenticator operation. This method may be removed in a future major release.")]
public async Task GetAuthenticatorByIdAsync(GetAuthenticatorByIdRequest request)
{
var requestObject = httpClient.GetRequest(endpointUrl + "authenticator/{auth_id}", Method.Get);
diff --git a/mailinator-csharp-client/Clients/ApiClients/Messages/MessagesClient.cs b/mailinator-csharp-client/Clients/ApiClients/Messages/MessagesClient.cs
index 81c2453..75f9cdd 100644
--- a/mailinator-csharp-client/Clients/ApiClients/Messages/MessagesClient.cs
+++ b/mailinator-csharp-client/Clients/ApiClients/Messages/MessagesClient.cs
@@ -23,6 +23,104 @@ public MessagesClient(IHttpClient httpClient, string endpointUrl)
this.endpointUrl = endpointUrl;
}
+ ///
+ /// Retrieves text content for a message in a domain.
+ ///
+ /// The domain and message ID.
+ /// Extracted message text, preserving any quoted-printable artifacts returned by the API.
+ public async Task GetMessageTextAsync(GetMessageTextRequest request)
+ {
+ var requestObject = httpClient.GetRequest(endpointUrl + "/{domain}/messages/{messageId}/text", Method.Get);
+ requestObject.AddUrlSegment("domain", request.Domain);
+ requestObject.AddUrlSegment("messageId", request.MessageId);
+
+ var response = await httpClient.ExecuteAsync(requestObject);
+ return response;
+ }
+
+ ///
+ /// Retrieves textplain content for a message in a domain.
+ ///
+ /// The domain and message ID.
+ /// The text/plain message body.
+ public async Task GetMessageTextPlainAsync(GetMessageTextPlainRequest request)
+ {
+ var requestObject = httpClient.GetRequest(endpointUrl + "/{domain}/messages/{messageId}/textplain", Method.Get);
+ requestObject.AddUrlSegment("domain", request.Domain);
+ requestObject.AddUrlSegment("messageId", request.MessageId);
+
+ var response = await httpClient.ExecuteAsync(requestObject);
+ return response;
+ }
+
+ ///
+ /// Retrieves texthtml content for a message in a domain.
+ ///
+ /// The domain and message ID.
+ /// The text/html message body, preserving HTML markup.
+ public async Task GetMessageTextHtmlAsync(GetMessageTextHtmlRequest request)
+ {
+ var requestObject = httpClient.GetRequest(endpointUrl + "/{domain}/messages/{messageId}/texthtml", Method.Get);
+ requestObject.AddUrlSegment("domain", request.Domain);
+ requestObject.AddUrlSegment("messageId", request.MessageId);
+
+ var response = await httpClient.ExecuteAsync(requestObject);
+ return response;
+ }
+
+ ///
+ /// Retrieves message metadata without body content.
+ ///
+ /// The domain and message ID.
+ /// The message summary.
+ public async Task GetMessageSummaryAsync(GetMessageSummaryRequest request)
+ {
+ var requestObject = httpClient.GetRequest(endpointUrl + "/{domain}/messages/{messageId}/summary", Method.Get);
+ requestObject.AddUrlSegment("domain", request.Domain);
+ requestObject.AddUrlSegment("messageId", request.MessageId);
+
+ var response = await httpClient.ExecuteAsync(requestObject);
+ return response;
+ }
+
+ ///
+ /// Retrieves SMTP headers for a message in a domain.
+ ///
+ /// The domain and message ID.
+ /// The message's headers.
+ public async Task GetMessageHeadersAsync(GetMessageHeadersRequest request)
+ {
+ var requestObject = httpClient.GetRequest(endpointUrl + "/{domain}/messages/{messageId}/headers", Method.Get);
+ requestObject.AddUrlSegment("domain", request.Domain);
+ requestObject.AddUrlSegment("messageId", request.MessageId);
+
+ var response = await httpClient.ExecuteAsync(requestObject);
+ return response;
+ }
+
+ ///
+ /// Retrieves message summaries across a domain, optionally filtered by inbox.
+ ///
+ /// Domain, optional inbox filter, and listing options.
+ /// Message summaries (or full messages when requested) and a pagination cursor.
+ public async Task ListDomainMessagesAsync(ListDomainMessagesRequest request)
+ {
+ var requestObject = httpClient.GetRequest(endpointUrl + "/{domain}/inboxes", Method.Get);
+ requestObject.AddUrlSegment("domain", request.Domain);
+ requestObject.AddSafeQueryParameter("inbox", request.Inbox);
+ requestObject.AddSafeQueryParameter("skip", request.Skip.ToString());
+ requestObject.AddSafeQueryParameter("limit", request.Limit.ToString());
+ requestObject.AddSafeQueryParameter("sort", request.Sort.ToString());
+ requestObject.AddSafeQueryParameter("decode_subject", request.DecodeSubject.ToString());
+ requestObject.AddSafeQueryParameter("cursor", request.Cursor);
+ requestObject.AddSafeQueryParameter("full", request.Full?.ToString());
+ requestObject.AddSafeQueryParameter("delete", request.Delete);
+ requestObject.AddSafeQueryParameter("wait", request.Wait);
+
+ var response = await httpClient.ExecuteAsync(requestObject);
+ return response;
+ }
+
///
/// This endpoint retrieves a list of messages summaries. You can retreive a list by inbox, inboxes, or entire domain.
/// :domain
@@ -66,6 +164,7 @@ public async Task FetchInboxMessageAsync(FetchInboxMe
requestObject.AddUrlSegment("domain", request.Domain);
requestObject.AddUrlSegment("inbox", request.Inbox);
requestObject.AddUrlSegment("messageId", request.MessageId);
+ requestObject.AddSafeQueryParameter("delete", request.Delete?.ToString());
var response = await httpClient.ExecuteAsync(requestObject);
return response;
diff --git a/mailinator-csharp-client/Clients/ApiClients/Webhooks/WebhooksClient.cs b/mailinator-csharp-client/Clients/ApiClients/Webhooks/WebhooksClient.cs
index 2f88c31..58baa0b 100644
--- a/mailinator-csharp-client/Clients/ApiClients/Webhooks/WebhooksClient.cs
+++ b/mailinator-csharp-client/Clients/ApiClients/Webhooks/WebhooksClient.cs
@@ -25,6 +25,31 @@ public WebhooksClient(IHttpClient httpClient, string endpointUrl)
this.endpointUrl = endpointUrl;
}
+ /// Injects a message using a webhook token in the domain segment or whtoken query parameter.
+ public async Task PostWebhookMessageAsync(PostWebhookMessageRequest request)
+ {
+ var requestObject = httpClient.GetRequest(endpointUrl + "/{domain}/webhook", Method.Post);
+ requestObject.AddUrlSegment("domain", request.Domain);
+ requestObject.AddSafeQueryParameter("whtoken", request.WebhookToken);
+ requestObject.AddJsonBody(request.Webhook);
+
+ var response = await httpClient.ExecuteAsync(requestObject);
+ return response;
+ }
+
+ /// Injects a message using a webhook token in the domain segment or whtoken query parameter.
+ public async Task PostWebhookInboxMessageAsync(PostWebhookInboxMessageRequest request)
+ {
+ var requestObject = httpClient.GetRequest(endpointUrl + "/{domain}/webhook/{inbox}", Method.Post);
+ requestObject.AddUrlSegment("domain", request.Domain);
+ requestObject.AddUrlSegment("inbox", request.Inbox);
+ requestObject.AddSafeQueryParameter("whtoken", request.WebhookToken);
+ requestObject.AddJsonBody(request.Webhook);
+
+ var response = await httpClient.ExecuteAsync(requestObject);
+ return response;
+ }
+
///
/// This command will Webhook messages into your Private Domain
/// The incoming Webhook will arrive in the inbox designated by the "to" field in the incoming request payload.
diff --git a/mailinator-csharp-client/Models/Messages/Requests/GetMessageHeadersRequest.cs b/mailinator-csharp-client/Models/Messages/Requests/GetMessageHeadersRequest.cs
new file mode 100644
index 0000000..5821a75
--- /dev/null
+++ b/mailinator-csharp-client/Models/Messages/Requests/GetMessageHeadersRequest.cs
@@ -0,0 +1,15 @@
+using Newtonsoft.Json;
+
+namespace mailinator_csharp_client.Models.Messages.Requests
+{
+ public class GetMessageHeadersRequest
+ {
+ /// The domain containing the message.
+ [JsonProperty("domain")]
+ public string Domain { get; set; }
+
+ /// The message ID returned by a message listing.
+ [JsonProperty("message_id")]
+ public string MessageId { get; set; }
+ }
+}
diff --git a/mailinator-csharp-client/Models/Messages/Requests/GetMessageSummaryRequest.cs b/mailinator-csharp-client/Models/Messages/Requests/GetMessageSummaryRequest.cs
new file mode 100644
index 0000000..59651f1
--- /dev/null
+++ b/mailinator-csharp-client/Models/Messages/Requests/GetMessageSummaryRequest.cs
@@ -0,0 +1,15 @@
+using Newtonsoft.Json;
+
+namespace mailinator_csharp_client.Models.Messages.Requests
+{
+ public class GetMessageSummaryRequest
+ {
+ /// The domain containing the message.
+ [JsonProperty("domain")]
+ public string Domain { get; set; }
+
+ /// The message ID returned by a message listing.
+ [JsonProperty("message_id")]
+ public string MessageId { get; set; }
+ }
+}
diff --git a/mailinator-csharp-client/Models/Messages/Requests/GetMessageTextHtmlRequest.cs b/mailinator-csharp-client/Models/Messages/Requests/GetMessageTextHtmlRequest.cs
new file mode 100644
index 0000000..ad76c27
--- /dev/null
+++ b/mailinator-csharp-client/Models/Messages/Requests/GetMessageTextHtmlRequest.cs
@@ -0,0 +1,15 @@
+using Newtonsoft.Json;
+
+namespace mailinator_csharp_client.Models.Messages.Requests
+{
+ public class GetMessageTextHtmlRequest
+ {
+ /// The domain containing the message.
+ [JsonProperty("domain")]
+ public string Domain { get; set; }
+
+ /// The message ID returned by a message listing.
+ [JsonProperty("message_id")]
+ public string MessageId { get; set; }
+ }
+}
diff --git a/mailinator-csharp-client/Models/Messages/Requests/GetMessageTextPlainRequest.cs b/mailinator-csharp-client/Models/Messages/Requests/GetMessageTextPlainRequest.cs
new file mode 100644
index 0000000..68dab8d
--- /dev/null
+++ b/mailinator-csharp-client/Models/Messages/Requests/GetMessageTextPlainRequest.cs
@@ -0,0 +1,15 @@
+using Newtonsoft.Json;
+
+namespace mailinator_csharp_client.Models.Messages.Requests
+{
+ public class GetMessageTextPlainRequest
+ {
+ /// The domain containing the message.
+ [JsonProperty("domain")]
+ public string Domain { get; set; }
+
+ /// The message ID returned by a message listing.
+ [JsonProperty("message_id")]
+ public string MessageId { get; set; }
+ }
+}
diff --git a/mailinator-csharp-client/Models/Messages/Requests/GetMessageTextRequest.cs b/mailinator-csharp-client/Models/Messages/Requests/GetMessageTextRequest.cs
new file mode 100644
index 0000000..ba50e94
--- /dev/null
+++ b/mailinator-csharp-client/Models/Messages/Requests/GetMessageTextRequest.cs
@@ -0,0 +1,15 @@
+using Newtonsoft.Json;
+
+namespace mailinator_csharp_client.Models.Messages.Requests
+{
+ public class GetMessageTextRequest
+ {
+ /// The domain containing the message.
+ [JsonProperty("domain")]
+ public string Domain { get; set; }
+
+ /// The message ID returned by a message listing.
+ [JsonProperty("message_id")]
+ public string MessageId { get; set; }
+ }
+}
diff --git a/mailinator-csharp-client/Models/Messages/Requests/ListDomainMessagesRequest.cs b/mailinator-csharp-client/Models/Messages/Requests/ListDomainMessagesRequest.cs
new file mode 100644
index 0000000..63d66ec
--- /dev/null
+++ b/mailinator-csharp-client/Models/Messages/Requests/ListDomainMessagesRequest.cs
@@ -0,0 +1,73 @@
+using mailinator_csharp_client.Models.Messages.Entities;
+using Newtonsoft.Json;
+
+namespace mailinator_csharp_client.Models.Messages.Requests
+{
+ public class ListDomainMessagesRequest
+ {
+ ///
+ /// public - Fetch Message Summaries from the Public Mailinator System
+ /// private - Fetch Message Summaries from all Your Private Domains
+ /// [your_private_domain.com] - Fetch Message Summaries from a specific Private Domain
+ ///
+ [JsonProperty("domain")]
+ public string Domain { get; set; } = "private";
+
+ ///
+ /// Omitted - fetch message summaries for the entire domain
+ /// * - Fetch All Messages summaries for an entire domain
+ /// [inbox_name] - Fetch All Messages summaries for a given Inbox
+ /// [inbox_name*] - Fetch All Messages summaries for a given Inbox Prefix
+ ///
+ [JsonProperty("inbox")]
+ public string Inbox { get; set; }
+
+ ///
+ /// Skip this many emails in your Private Domain. Default Value 0. Required - no
+ ///
+ [JsonProperty("skip")]
+ public int Skip { get; set; } = 0;
+
+ ///
+ /// Number of emails to fetch from your Private Domain. Default Value 50. Required - no
+ ///
+ [JsonProperty("limit")]
+ public int Limit { get; set; } = 50;
+
+ ///
+ /// Sort results by ascending or descending. Default Value 'descending'. Required - no
+ ///
+ [JsonProperty("sort")]
+ public Sort Sort { get; set; } = Sort.desc;
+
+ ///
+ /// true: decode encoded subjects. Default Value 'false'. Required - no
+ ///
+ [JsonProperty("decode_subject")]
+ public bool DecodeSubject { get; set; } = false;
+
+ ///
+ /// Pagination cursor for large result sets (obtained from previous response). Required - no
+ ///
+ [JsonProperty("cursor")]
+ public string Cursor { get; set; }
+
+ ///
+ /// Return full email content with body/attachments (true) or just metadata (false). Default: false. Required - no
+ ///
+ [JsonProperty("full")]
+ public bool? Full { get; set; }
+
+ ///
+ /// Auto-delete message after retrieval (e.g., "10s" = 10 seconds, "5m" = 5 minutes). Required - no
+ ///
+ [JsonProperty("delete")]
+ public string Delete { get; set; }
+
+ ///
+ /// Maximum time to wait for new messages (e.g., "30s" = 30 seconds). Required - no
+ ///
+ [JsonProperty("wait")]
+ public string Wait { get; set; }
+ }
+}
diff --git a/mailinator-csharp-client/Models/Messages/Responses/GetMessageHeadersResponse.cs b/mailinator-csharp-client/Models/Messages/Responses/GetMessageHeadersResponse.cs
new file mode 100644
index 0000000..850e01c
--- /dev/null
+++ b/mailinator-csharp-client/Models/Messages/Responses/GetMessageHeadersResponse.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace mailinator_csharp_client.Models.Responses
+{
+ public class GetMessageHeadersResponse
+ {
+ /// SMTP headers, including custom headers and multi-valued headers such as received.
+ [JsonProperty("headers")]
+ public Dictionary Headers;
+ }
+}
diff --git a/mailinator-csharp-client/Models/Messages/Responses/GetMessageSummaryResponse.cs b/mailinator-csharp-client/Models/Messages/Responses/GetMessageSummaryResponse.cs
new file mode 100644
index 0000000..c1fc6f5
--- /dev/null
+++ b/mailinator-csharp-client/Models/Messages/Responses/GetMessageSummaryResponse.cs
@@ -0,0 +1,12 @@
+using mailinator_csharp_client.Models.Messages.Entities;
+using Newtonsoft.Json;
+
+namespace mailinator_csharp_client.Models.Responses
+{
+ public class GetMessageSummaryResponse
+ {
+ /// Message metadata. Body and attachment content are not returned by this endpoint.
+ [JsonProperty("summary")]
+ public Message Summary;
+ }
+}
diff --git a/mailinator-csharp-client/Models/Messages/Responses/GetMessageTextHtmlResponse.cs b/mailinator-csharp-client/Models/Messages/Responses/GetMessageTextHtmlResponse.cs
new file mode 100644
index 0000000..1155711
--- /dev/null
+++ b/mailinator-csharp-client/Models/Messages/Responses/GetMessageTextHtmlResponse.cs
@@ -0,0 +1,11 @@
+using Newtonsoft.Json;
+
+namespace mailinator_csharp_client.Models.Responses
+{
+ public class GetMessageTextHtmlResponse
+ {
+ /// The text/html message body, preserving HTML markup.
+ [JsonProperty("text/html")]
+ public string TextHtml { get; set; }
+ }
+}
diff --git a/mailinator-csharp-client/Models/Messages/Responses/GetMessageTextPlainResponse.cs b/mailinator-csharp-client/Models/Messages/Responses/GetMessageTextPlainResponse.cs
new file mode 100644
index 0000000..e270e42
--- /dev/null
+++ b/mailinator-csharp-client/Models/Messages/Responses/GetMessageTextPlainResponse.cs
@@ -0,0 +1,11 @@
+using Newtonsoft.Json;
+
+namespace mailinator_csharp_client.Models.Responses
+{
+ public class GetMessageTextPlainResponse
+ {
+ /// The text/plain message body.
+ [JsonProperty("text/plain")]
+ public string TextPlain { get; set; }
+ }
+}
diff --git a/mailinator-csharp-client/Models/Messages/Responses/GetMessageTextResponse.cs b/mailinator-csharp-client/Models/Messages/Responses/GetMessageTextResponse.cs
new file mode 100644
index 0000000..70f8c1b
--- /dev/null
+++ b/mailinator-csharp-client/Models/Messages/Responses/GetMessageTextResponse.cs
@@ -0,0 +1,11 @@
+using Newtonsoft.Json;
+
+namespace mailinator_csharp_client.Models.Responses
+{
+ public class GetMessageTextResponse
+ {
+ /// Extracted message text, preserving any quoted-printable artifacts returned by the API.
+ [JsonProperty("text")]
+ public string Text { get; set; }
+ }
+}
diff --git a/mailinator-csharp-client/Models/Webhooks/Entities/WebhookMessage.cs b/mailinator-csharp-client/Models/Webhooks/Entities/WebhookMessage.cs
new file mode 100644
index 0000000..8708af3
--- /dev/null
+++ b/mailinator-csharp-client/Models/Webhooks/Entities/WebhookMessage.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace mailinator_csharp_client.Models.Webhooks.Entities
+{
+ /// Payload for domain and inbox webhook injection.
+ public class WebhookMessage : Webhook
+ {
+ [JsonProperty("html")]
+ public string Html { get; set; }
+
+ [JsonProperty("headers")]
+ public Dictionary Headers { get; set; }
+
+ [JsonExtensionData]
+ public IDictionary AdditionalProperties { get; set; }
+ }
+}
diff --git a/mailinator-csharp-client/Models/Webhooks/Requests/PostWebhookInboxMessageRequest.cs b/mailinator-csharp-client/Models/Webhooks/Requests/PostWebhookInboxMessageRequest.cs
new file mode 100644
index 0000000..0867eb5
--- /dev/null
+++ b/mailinator-csharp-client/Models/Webhooks/Requests/PostWebhookInboxMessageRequest.cs
@@ -0,0 +1,10 @@
+using Newtonsoft.Json;
+
+namespace mailinator_csharp_client.Models.Webhooks.Requests
+{
+ public class PostWebhookInboxMessageRequest : PostWebhookMessageRequest
+ {
+ [JsonProperty("inbox")]
+ public string Inbox { get; set; }
+ }
+}
diff --git a/mailinator-csharp-client/Models/Webhooks/Requests/PostWebhookMessageRequest.cs b/mailinator-csharp-client/Models/Webhooks/Requests/PostWebhookMessageRequest.cs
new file mode 100644
index 0000000..d1a233f
--- /dev/null
+++ b/mailinator-csharp-client/Models/Webhooks/Requests/PostWebhookMessageRequest.cs
@@ -0,0 +1,20 @@
+using mailinator_csharp_client.Models.Webhooks.Entities;
+using Newtonsoft.Json;
+
+namespace mailinator_csharp_client.Models.Webhooks.Requests
+{
+ public class PostWebhookMessageRequest
+ {
+ /// Private domain name, or a webhook token when WebhookToken is omitted.
+ [JsonProperty("domain")]
+ public string Domain { get; set; }
+
+ /// Optional webhook token sent as whtoken when Domain is a domain name.
+ [JsonProperty("whtoken")]
+ public string WebhookToken { get; set; }
+
+ /// Message payload. Set To to the recipient inbox.
+ [JsonIgnore]
+ public WebhookMessage Webhook { get; set; }
+ }
+}
diff --git a/mailinator-csharp-client/Models/Webhooks/Responses/PostWebhookMessageResponse.cs b/mailinator-csharp-client/Models/Webhooks/Responses/PostWebhookMessageResponse.cs
new file mode 100644
index 0000000..9d40d28
--- /dev/null
+++ b/mailinator-csharp-client/Models/Webhooks/Responses/PostWebhookMessageResponse.cs
@@ -0,0 +1,13 @@
+using Newtonsoft.Json;
+
+namespace mailinator_csharp_client.Models.Webhooks.Responses
+{
+ public class PostWebhookMessageResponse
+ {
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ [JsonProperty("status")]
+ public string Status { get; set; }
+ }
+}
diff --git a/mailinator-csharp-client/mailinator-csharp-client.csproj b/mailinator-csharp-client/mailinator-csharp-client.csproj
index 4021225..a09c66c 100644
--- a/mailinator-csharp-client/mailinator-csharp-client.csproj
+++ b/mailinator-csharp-client/mailinator-csharp-client.csproj
@@ -15,15 +15,16 @@
MIT
README.md
MailinatorApiClient
- 1.0.7
- 1.0.7
- 1.0.7
+ 1.0.8
+ 1.0.8
+ 1.0.8
True
-
-
+
+
+
diff --git a/mailinator-csharp-client/packages.lock.json b/mailinator-csharp-client/packages.lock.json
new file mode 100644
index 0000000..8533912
--- /dev/null
+++ b/mailinator-csharp-client/packages.lock.json
@@ -0,0 +1,225 @@
+{
+ "version": 1,
+ "dependencies": {
+ ".NETFramework,Version=v4.7.1": {
+ "Microsoft.NETFramework.ReferenceAssemblies": {
+ "type": "Direct",
+ "requested": "[1.0.3, )",
+ "resolved": "1.0.3",
+ "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+ "dependencies": {
+ "Microsoft.NETFramework.ReferenceAssemblies.net471": "1.0.3"
+ }
+ },
+ "Newtonsoft.Json": {
+ "type": "Direct",
+ "requested": "[13.0.4, )",
+ "resolved": "13.0.4",
+ "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A=="
+ },
+ "RestSharp": {
+ "type": "Direct",
+ "requested": "[114.0.0, )",
+ "resolved": "114.0.0",
+ "contentHash": "8pDO4q+K8nIqXbvGCb9ciKcngj36pCXGRDzOYPEPoS2IvBIL+mejmUMAtSNALt/NkDc9LQmHvM95kdniLSbGtg==",
+ "dependencies": {
+ "System.Text.Json": "10.0.0"
+ }
+ },
+ "System.Text.Json": {
+ "type": "Direct",
+ "requested": "[10.0.11, )",
+ "resolved": "10.0.11",
+ "contentHash": "X2rXQy7g8KEUvWVbAz6vOk6byI1eMgmzFeXbKxq4BfZCfFy5S91K+K+hd4ZBlSp0wL1Y8FyGRXs6+hvABTjwQw==",
+ "dependencies": {
+ "Microsoft.Bcl.AsyncInterfaces": "10.0.11",
+ "System.Buffers": "4.6.1",
+ "System.IO.Pipelines": "10.0.11",
+ "System.Memory": "4.6.3",
+ "System.Runtime.CompilerServices.Unsafe": "6.1.2",
+ "System.Text.Encodings.Web": "10.0.11",
+ "System.Threading.Tasks.Extensions": "4.6.3",
+ "System.ValueTuple": "4.6.2"
+ }
+ },
+ "Microsoft.Bcl.AsyncInterfaces": {
+ "type": "Transitive",
+ "resolved": "10.0.11",
+ "contentHash": "pUkYI1f99hY9giu/MRaEVN53RO7skXwR7FGtVvFJCK5ezdg5i0W+z1yj/91X5aKv07xGuSB5n1g/Zt2P4Ooq6g==",
+ "dependencies": {
+ "System.Threading.Tasks.Extensions": "4.6.3"
+ }
+ },
+ "Microsoft.NETFramework.ReferenceAssemblies.net471": {
+ "type": "Transitive",
+ "resolved": "1.0.3",
+ "contentHash": "Kf3vusy+mwHzn+0osmJAMrZNG00/UkwywjwfFKLUWSYEXiIMD6SEMGP9bG3sH1B54IiydxAneLubS5/Ajjn/Tw=="
+ },
+ "System.Buffers": {
+ "type": "Transitive",
+ "resolved": "4.6.1",
+ "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw=="
+ },
+ "System.IO.Pipelines": {
+ "type": "Transitive",
+ "resolved": "10.0.11",
+ "contentHash": "rg8LPOZ62quw3aTnsQNh9rssncaMs69WEM1DiJdDkV+o4Llb2s5Pasy5Bulfm8zL+pE4gDcSQo7V2zW2jvxwYg==",
+ "dependencies": {
+ "System.Buffers": "4.6.1",
+ "System.Memory": "4.6.3",
+ "System.Threading.Tasks.Extensions": "4.6.3"
+ }
+ },
+ "System.Memory": {
+ "type": "Transitive",
+ "resolved": "4.6.3",
+ "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==",
+ "dependencies": {
+ "System.Buffers": "4.6.1",
+ "System.Numerics.Vectors": "4.6.1",
+ "System.Runtime.CompilerServices.Unsafe": "6.1.2"
+ }
+ },
+ "System.Numerics.Vectors": {
+ "type": "Transitive",
+ "resolved": "4.6.1",
+ "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q=="
+ },
+ "System.Runtime.CompilerServices.Unsafe": {
+ "type": "Transitive",
+ "resolved": "6.1.2",
+ "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw=="
+ },
+ "System.Text.Encodings.Web": {
+ "type": "Transitive",
+ "resolved": "10.0.11",
+ "contentHash": "R7H2/Oqr3onFff/JKDpfRjwxcQZlocF/eQ0ce8go/OTjqz+6LEp/fZK8j1YnDobtysChATEg7krVq4hQJ3IoeQ==",
+ "dependencies": {
+ "System.Buffers": "4.6.1",
+ "System.Memory": "4.6.3",
+ "System.Runtime.CompilerServices.Unsafe": "6.1.2"
+ }
+ },
+ "System.Threading.Tasks.Extensions": {
+ "type": "Transitive",
+ "resolved": "4.6.3",
+ "contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==",
+ "dependencies": {
+ "System.Runtime.CompilerServices.Unsafe": "6.1.2"
+ }
+ },
+ "System.ValueTuple": {
+ "type": "Transitive",
+ "resolved": "4.6.2",
+ "contentHash": "yQgmjfFximrNm9LIV3mL6T5MzjeC+epeE5rl4hXxAlYmxby7RM1dPSkIKXk9HNkl6G54h2JHOmLD46+Pey+IRg=="
+ }
+ },
+ ".NETStandard,Version=v2.0": {
+ "NETStandard.Library": {
+ "type": "Direct",
+ "requested": "[2.0.3, )",
+ "resolved": "2.0.3",
+ "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
+ "dependencies": {
+ "Microsoft.NETCore.Platforms": "1.1.0"
+ }
+ },
+ "Newtonsoft.Json": {
+ "type": "Direct",
+ "requested": "[13.0.4, )",
+ "resolved": "13.0.4",
+ "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A=="
+ },
+ "RestSharp": {
+ "type": "Direct",
+ "requested": "[114.0.0, )",
+ "resolved": "114.0.0",
+ "contentHash": "8pDO4q+K8nIqXbvGCb9ciKcngj36pCXGRDzOYPEPoS2IvBIL+mejmUMAtSNALt/NkDc9LQmHvM95kdniLSbGtg==",
+ "dependencies": {
+ "System.Text.Json": "10.0.0"
+ }
+ },
+ "System.Text.Json": {
+ "type": "Direct",
+ "requested": "[10.0.11, )",
+ "resolved": "10.0.11",
+ "contentHash": "X2rXQy7g8KEUvWVbAz6vOk6byI1eMgmzFeXbKxq4BfZCfFy5S91K+K+hd4ZBlSp0wL1Y8FyGRXs6+hvABTjwQw==",
+ "dependencies": {
+ "Microsoft.Bcl.AsyncInterfaces": "10.0.11",
+ "System.Buffers": "4.6.1",
+ "System.IO.Pipelines": "10.0.11",
+ "System.Memory": "4.6.3",
+ "System.Runtime.CompilerServices.Unsafe": "6.1.2",
+ "System.Text.Encodings.Web": "10.0.11",
+ "System.Threading.Tasks.Extensions": "4.6.3"
+ }
+ },
+ "Microsoft.Bcl.AsyncInterfaces": {
+ "type": "Transitive",
+ "resolved": "10.0.11",
+ "contentHash": "pUkYI1f99hY9giu/MRaEVN53RO7skXwR7FGtVvFJCK5ezdg5i0W+z1yj/91X5aKv07xGuSB5n1g/Zt2P4Ooq6g==",
+ "dependencies": {
+ "System.Threading.Tasks.Extensions": "4.6.3"
+ }
+ },
+ "Microsoft.NETCore.Platforms": {
+ "type": "Transitive",
+ "resolved": "1.1.0",
+ "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
+ },
+ "System.Buffers": {
+ "type": "Transitive",
+ "resolved": "4.6.1",
+ "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw=="
+ },
+ "System.IO.Pipelines": {
+ "type": "Transitive",
+ "resolved": "10.0.11",
+ "contentHash": "rg8LPOZ62quw3aTnsQNh9rssncaMs69WEM1DiJdDkV+o4Llb2s5Pasy5Bulfm8zL+pE4gDcSQo7V2zW2jvxwYg==",
+ "dependencies": {
+ "System.Buffers": "4.6.1",
+ "System.Memory": "4.6.3",
+ "System.Threading.Tasks.Extensions": "4.6.3"
+ }
+ },
+ "System.Memory": {
+ "type": "Transitive",
+ "resolved": "4.6.3",
+ "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==",
+ "dependencies": {
+ "System.Buffers": "4.6.1",
+ "System.Numerics.Vectors": "4.6.1",
+ "System.Runtime.CompilerServices.Unsafe": "6.1.2"
+ }
+ },
+ "System.Numerics.Vectors": {
+ "type": "Transitive",
+ "resolved": "4.6.1",
+ "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q=="
+ },
+ "System.Runtime.CompilerServices.Unsafe": {
+ "type": "Transitive",
+ "resolved": "6.1.2",
+ "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw=="
+ },
+ "System.Text.Encodings.Web": {
+ "type": "Transitive",
+ "resolved": "10.0.11",
+ "contentHash": "R7H2/Oqr3onFff/JKDpfRjwxcQZlocF/eQ0ce8go/OTjqz+6LEp/fZK8j1YnDobtysChATEg7krVq4hQJ3IoeQ==",
+ "dependencies": {
+ "System.Buffers": "4.6.1",
+ "System.Memory": "4.6.3",
+ "System.Runtime.CompilerServices.Unsafe": "6.1.2"
+ }
+ },
+ "System.Threading.Tasks.Extensions": {
+ "type": "Transitive",
+ "resolved": "4.6.3",
+ "contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==",
+ "dependencies": {
+ "System.Runtime.CompilerServices.Unsafe": "6.1.2"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file