From 187949ee25bebb5854540006e62d1ca5274be868 Mon Sep 17 00:00:00 2001 From: Chris Kenst Date: Mon, 10 Aug 2026 16:52:03 -0700 Subject: [PATCH 1/2] Update dependencies for 1.1.1 --- CHANGELOG.md | 12 ++++++++++++ lib/mailinator_client/version.rb | 2 +- mailinator_client.gemspec | 11 ++++++----- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3af9e7d..a8a5c3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project aims to follow [Semantic Versioning](https://semver.org/). +## [1.1.1] + +### Security + +- Updated `httparty` to `>= 0.24, < 0.25` to address the SSRF and API-key leakage vulnerability in CVE-2025-68696. +- Required `addressable >= 2.9, < 3.0` for development to address the URI-template ReDoS vulnerability in CVE-2026-35611. + +### Changed + +- Raised the minimum supported Ruby version from 2.1 to 2.7, matching the requirement of the fixed `httparty` release line. +- Updated development dependency floors for `minitest`, `rake`, and `webmock`. + ## [1.1.0] ### Changed diff --git a/lib/mailinator_client/version.rb b/lib/mailinator_client/version.rb index 620616e..e0366ce 100644 --- a/lib/mailinator_client/version.rb +++ b/lib/mailinator_client/version.rb @@ -1,3 +1,3 @@ module MailinatorClient - VERSION = "1.1.0" + VERSION = "1.1.1" end diff --git a/mailinator_client.gemspec b/mailinator_client.gemspec index 11f61b3..4dad522 100644 --- a/mailinator_client.gemspec +++ b/mailinator_client.gemspec @@ -15,11 +15,12 @@ Gem::Specification.new do |gem| gem.version = MailinatorClient::VERSION gem.licenses = ["MIT"] - gem.required_ruby_version = ">= 2.1" + gem.required_ruby_version = ">= 2.7" - gem.add_dependency "httparty", ">= 0.21", "< 0.22" + gem.add_dependency "httparty", ">= 0.24", "< 0.25" - gem.add_development_dependency "minitest", ">= 5.25", "< 7.0" - gem.add_development_dependency "rake", ">= 13.0", "< 14.0" - gem.add_development_dependency "webmock", ">= 3.26", "< 4.0" + gem.add_development_dependency "addressable", ">= 2.9", "< 3.0" + gem.add_development_dependency "minitest", ">= 5.26", "< 6.0" + gem.add_development_dependency "rake", ">= 13.4", "< 14.0" + gem.add_development_dependency "webmock", ">= 3.26.2", "< 4.0" end From 8c594f2961521ba5b5ca2b5386abd908073eef8a Mon Sep 17 00:00:00 2001 From: Chris Kenst Date: Wed, 12 Aug 2026 17:41:03 -0700 Subject: [PATCH 2/2] Consolidate AI guidance --- .agent/tdd-flow/skill.md | 4 +- AGENTS.md | 22 ++-- AI_INSTRUCTIONS.md | 205 ------------------------------------ README.md | 8 +- ROADMAP.md | 4 +- docs/openapi-maintenance.md | 150 ++++++++++++++++++++++++++ 6 files changed, 173 insertions(+), 220 deletions(-) delete mode 100644 AI_INSTRUCTIONS.md create mode 100644 docs/openapi-maintenance.md diff --git a/.agent/tdd-flow/skill.md b/.agent/tdd-flow/skill.md index 368a6a8..0f45763 100644 --- a/.agent/tdd-flow/skill.md +++ b/.agent/tdd-flow/skill.md @@ -7,9 +7,9 @@ triggers: ## Workflow Steps 1. **Red Phase:** Write ONE failing test. Explain the failure. **STOP.** -- Requirement: All tests generated in this phase must adhere to the Test Expectations defined in the ai_instruction file (specifically: No mocking, real HTTP requests, and semantic validation). +- Requirement: All tests generated in this phase must follow the test expectations in `AGENTS.md` (specifically: no mocked endpoint integration tests, real HTTP requests, and semantic validation). 2. **Green Phase:** Write the simplest possible implementation to pass that specific test. **STOP.** 3. **Refactor Phase:** Suggest improvements to the implementation. Do not change tests. ## Constraint -- Do not jump to Step 2 until the user confirms Step 1 passes (or fails correctly). \ No newline at end of file +- Do not jump to Step 2 until the user confirms Step 1 passes (or fails correctly). diff --git a/AGENTS.md b/AGENTS.md index 2d81eca..2e3bd2b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,9 +1,17 @@ -All AI agents (Codex, Copilot, Antigravity) must adhere to the rules defined in .agent/rules/ and this file. +All AI agents (Codex, Copilot, Antigravity) must follow this file and the rules in `.agent/rules/`. -# Project Standards & Agent Behavior +# Project Standards and Agent Behavior -- **Primary Workflow:** We use the `tdd-flow` skill for all new feature development. -- **Test Style:** Focus on behavior-driven assertions. No mocking. -- **Anti-Pattern Guardrail:** Do not "hallucinate" implementation for skipped tests. If a test is ignored, the underlying code must remain untouched. -- **Language/Framework:** Ruby with RSpec for testing. -- **Spec-Strictness:** When generating tests for the SDK, only assert properties explicitly defined in the OpenAPI specification provided. Do not invent "common sense" validations that are not codified in the schema. Call out any ambiguities or gaps in the spec for human review instead of making assumptions. \ No newline at end of file +- **Language and tests:** This is a Ruby SDK tested with Minitest. +- **Primary workflow:** Use the `.agent/tdd-flow` skill for feature implementation and bug fixes, subject to `.agent/rules/tdd.md`. +- **Test style:** Write behavior-driven assertions. Endpoint integration tests must use real HTTP requests; do not mock them. +- **Skipped tests:** Never infer or change implementation for a skipped test. Leave the underlying code untouched. +- **OpenAPI source of truth:** Use the [Mailinator OpenAPI specification](https://raw.githubusercontent.com/manybrain/mailinatordocs/main/openapi/mailinator-api.yaml). +- **Spec strictness:** Only assert behavior and properties explicitly defined by the OpenAPI specification. Do not invent validations. Flag ambiguities or gaps for human review. +- **Request paths:** Resource wrappers use paths relative to `https://api.mailinator.com/api/v2`; do not include `/api/v2` or `/v2` in resource method paths. +- **Gap-analysis changes:** Follow `docs/openapi-maintenance.md`. Present the implementation plan and wait for approval before changing SDK coverage. + +# Project References + +- `docs/openapi-maintenance.md` documents the SDK architecture, conventions, and OpenAPI gap-analysis workflow. +- `ROADMAP.md` tracks known gaps and planned improvements. diff --git a/AI_INSTRUCTIONS.md b/AI_INSTRUCTIONS.md deleted file mode 100644 index 7d6ce5b..0000000 --- a/AI_INSTRUCTIONS.md +++ /dev/null @@ -1,205 +0,0 @@ -# AI Instructions - -This document explains the relationship between this Ruby client and the Mailinator OpenAPI specification. - -**OpenAPI Specification:** [Found on GitHub](https://github.com/manybrain/mailinatordocs/blob/main/openapi/mailinator-api.yaml) - -## Codebase Structure - -The codebase structure in `lib/mailinator_client/` reflects Mailinator API resource groups. - -- **Entrypoints:** - - `lib/mailinator_client.rb` loads all components and provides module-level delegation to a singleton `Client`. - - `lib/mailinator_client/client.rb` defines the HTTP execution path and shared request behavior. -- **Resource wrappers:** API resources live in peer files under `lib/mailinator_client/`: - - `authenticators.rb` for authenticator endpoints. - - `domains.rb` for domain endpoints. - - `messages.rb` for inbox/message endpoints. - - `rules.rb` for rule endpoints. - - `stats.rb` for team/stat endpoints. - - `webhooks.rb` for webhook injection endpoints. -- **Support files:** - - `utils.rb` normalizes input/query structures. - - `error.rb` defines `ResponseError`. - - `version.rb` defines gem version metadata used in user agent headers. - -## Request Patterns - -This client uses a **resource wrapper** pattern, not per-operation request classes. - -- Each resource class exposes Ruby methods (for example, `fetch_inbox`, `get_domains`, `create_rule`) that: - - accept a params hash, - - validate required keys with `ArgumentError`, - - construct `path`, `query`, and optional `body`, - - call `@client.request(...)`. -- Method names are snake_case and generally map to one API operation each. -- Paths are resource-relative and are joined to the client base URL (`https://api.mailinator.com/api/v2`) inside `Client#request`. - -## Execution - -Requests are executed through `MailinatorClient::Client#request`, which uses `HTTParty`. - -```ruby -client = MailinatorClient::Client.new(auth_token: "api_token") -response = client.messages.fetch_inbox(domain: "domain.com", inbox: "inbox_name") -``` - -Execution flow: -- Resource method builds request inputs (`method`, `path`, `query`, `headers`, `body`). -- `Client#request` appends the path to `https://api.mailinator.com/api/v2`. -- `HTTParty.send` performs the HTTP call with JSON headers and optional authorization. -- Non-2xx/3xx responses raise `MailinatorClient::ResponseError`. - -## Entities - -This Ruby SDK mostly returns parsed response hashes/arrays directly, rather than strongly typed model classes. - -- API responses are returned as Ruby data structures from `HTTParty` (`Hash`/`Array`). -- Error responses are wrapped in `MailinatorClient::ResponseError` with: - - `code` (HTTP status), - - `type` (error type from API payload), - - exception message from the API response. -- Request inputs are plain Ruby hashes, typically normalized by `Utils.symbolize_hash_keys`. - ---- - -## Gap Analysis Workflow - -Use this workflow whenever you want to audit the SDK against the OpenAPI spec, identify missing or extra coverage, and bring the two into alignment. - -### Step 1 — Fetch the OpenAPI Specification - -Retrieve the raw YAML from: - -``` -https://raw.githubusercontent.com/manybrain/mailinatordocs/main/openapi/mailinator-api.yaml -``` - -> The rendered GitHub page is at https://github.com/manybrain/mailinatordocs/blob/main/openapi/mailinator-api.yaml -> but always read the **raw** URL for machine parsing. - -Extract every `paths` entry. For each path, record: -- The HTTP method (`get`, `post`, `put`, `delete`, etc.) -- The full path string (e.g. `/api/v2/domains/{domain}/inboxes/{inbox}`) -- The `operationId` -- The tag (maps to the SDK module directory) -- All query parameters defined under `parameters` - -### Step 2 — Catalog the SDK - -For each resource file under `lib/mailinator_client/` (`messages.rb`, `domains.rb`, `rules.rb`, etc.): -1. Enumerate every public method that issues `@client.request(...)`. -2. Identify the HTTP method (`:get`, `:post`, `:put`, `:delete`). -3. Extract the `path` template used by that method. -4. Record query parameters populated in `query_params`. -5. Note methods already marked deprecated in comments/docs. - -Also map resource files to OpenAPI tags and check if any tag has no SDK wrapper. - -### Step 3 — Identify Gaps - -Produce a gap report with four sections: - -#### A. In the spec but missing from the SDK -List every `operationId` that has no corresponding Ruby method. This is what needs to be **added**. - -#### B. In the SDK but not in the spec -List every SDK method whose path+method has no matching entry in the spec. -- If it is already marked deprecated, note that separately. -- If it is not deprecated but absent from the spec, flag it for clarification (it may be undocumented). - -#### C. URL path mismatches -Compare the base path used by each SDK method against the spec. -- The spec base URL is `https://api.mailinator.com` and all paths start with `/api/v2/`. -- The SDK **must** use `/api/v2/` not `/v2/`. Flag any method/path using the wrong prefix. - -#### D. Query parameter gaps -For each existing SDK method, compare sent query parameters against the spec's declared parameters for that operation. List any missing parameters. - -#### Exception — Domain Listing -The OpenAPI operation `GET /api/v2/domains/{domain}/inboxes` (list domain messages) is considered **covered** by `messages.fetch_inbox` when called with `inbox: "*"`. Do not treat this as a missing SDK method in future gap analyses. - -### Step 4 — Build a Plan - -Before making any changes, write out a plan that includes: - -1. **New methods to add** — one method per missing `operationId`, grouped by resource file. -2. **URL fixes** — list every file where the prefix needs to change from `/v2/` to `/api/v2/`. -3. **Query parameter additions** — list every file and which parameters to add. -4. **Deprecated methods** — decide whether to keep and mark as deprecated or remove. Do not remove without confirmation. -5. **Response/entity notes** — list response shape expectations or wrappers needed for consistency. - -Present the plan to the user and wait for approval before proceeding. - -### Step 5 — Implement - -Follow the existing patterns in the codebase: - -#### Adding a new method - -Use an existing method in the matching resource file as a template. - -```ruby -def get_example(params = {}) - params = Utils.symbolize_hash_keys(params) - query_params = {} - headers = {} - body = nil - - raise ArgumentError.new("domain is required") unless params.has_key?(:domain) - raise ArgumentError.new("id is required") unless params.has_key?(:id) - - path = "/domains/#{params[:domain]}/examples/#{params[:id]}" - - @client.request( - method: :get, - path: path, - query: query_params, - headers: headers, - body: body - ) -end -``` - -Key rules: -- **Always** use `/api/v2/` as the path prefix — never `/v2/`. -- Keep methods in the appropriate resource file (`messages.rb`, `rules.rb`, etc.). -- Validate required params with `ArgumentError`. -- Use snake_case method names and preserve existing naming conventions in the file. - -#### Fixing a URL prefix - -If any hardcoded URL includes `/v2/`, change it to `/api/v2/`. Prefer resource-relative `path` values (`/domains/...`) and let `Client#request` prepend base URL. - -#### Adding a missing query parameter - -Add an assignment in the method's query assembly: -```ruby -query_params[:my_param] = params[:myParam] if params.has_key?(:myParam) -``` -Then document the optional parameter in the method comment block. - -### Step 6 — Verify - -After implementing: -1. Run tests: `ruby -I test test/mailinator_client_api_test.rb` (with required env vars). -2. Run lint/static checks if configured for this repo. -3. Manually verify at least one changed method generates the exact spec path and sends expected query params. - -### Notes on SDK Conventions - -| Convention | Detail | -|---|---| -| Version source | `lib/mailinator_client/version.rb` (`MailinatorClient::VERSION`), referenced by gemspec and user-agent string. | -| Auth header | Set in `Client#request` as `Authorization` when `auth_token` is provided. | -| No-token requests | Supported by instantiating `Client` without `auth_token` (used by some webhook flows). | -| Deprecation marker | Use Ruby/YARD style deprecation comments near method definitions and reflect in README/docs. | -| Entrypoint loading | `lib/mailinator_client.rb` requires resource/support files and delegates module methods to singleton client. | - -### Test Expectations - -- Integration tests should exercise real HTTP requests to Mailinator endpoints. Do not use request-mocking tools (for example, `WebMock.stub_request`) for endpoint coverage tests. -- Assertions must validate response semantics, not just existence. Prefer checking: - - expected HTTP success behavior (or explicit failure with returned status code), - - expected JSON shape (required keys), - - important field-level values (for example, IDs or arrays) relevant to the endpoint contract. diff --git a/README.md b/README.md index 543db00..616345e 100644 --- a/README.md +++ b/README.md @@ -90,19 +90,19 @@ When the Mailinator API returns a unsuccessful response, an instance of Response ## Testing -Run individual integration tests with real API Key. +Run an individual integration test with a real API key: ```ruby -ruby -I test test/mailinator_client_api_test.rb +bundle exec ruby -Itest test/messages_api_test.rb ``` -OR run them all: +Or run the full test suite: ```ruby bundle exec rake test ``` -Most of the tests require env variables with valid values. Visit tests source code and review `mailinator_client_api_test.rb` file. The more env variables you set, the more tests are run. +Most integration tests require environment variables with valid values. Review the files under `test/` and `.env.example`; the tests whose required variables are unavailable will be skipped. * `MAILINATOR_TEST_API_TOKEN` - API tokens for authentication; basic requirement across many tests;see also https://manybrain.github.io/m8rdocs/#api-authentication * `MAILINATOR_TEST_INBOX` - some already existing inbox within the private domain diff --git a/ROADMAP.md b/ROADMAP.md index bb19ca3..d7d8a72 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -30,7 +30,7 @@ Webhooks (`lib/mailinator_client/webhooks.rb`): ## Completed (Phase 1) -- [x] Add structural docs (`ROADMAP.md`, `CHANGELOG.md`, `AI_INSTRUCTIONS.md`, `EXAMPLES.md`) +- [x] Add structural docs (`ROADMAP.md`, `CHANGELOG.md`, `docs/openapi-maintenance.md`, `EXAMPLES.md`) - [x] Update outdated dependencies (`rake` and `webmock`; `httparty` pinned to latest Ruby 2.6-compatible range) - [x] Update version number (`1.0.7`) - [x] Publish those changes (minor release) @@ -55,4 +55,4 @@ Messages (`lib/mailinator_client/messages.rb`): - [x] Add `stream_inbox_messages` for `GET /domains/{domain}/stream/{inbox}` - [x] Add `list_domain_messages` for `GET /domains/{domain}/inboxes` (covered by `messages.fetch_inbox` with `inbox: "*"`) -Note: We are intentionally not adding a separate SDK method for `GET /domains/{domain}/inboxes`. The existing `messages.fetch_inbox` with `inbox: "*"` provides equivalent domain-wide listing. Future gap analyses should treat this as covered. \ No newline at end of file +Note: We are intentionally not adding a separate SDK method for `GET /domains/{domain}/inboxes`. The existing `messages.fetch_inbox` with `inbox: "*"` provides equivalent domain-wide listing. Future gap analyses should treat this as covered. diff --git a/docs/openapi-maintenance.md b/docs/openapi-maintenance.md new file mode 100644 index 0000000..5758af4 --- /dev/null +++ b/docs/openapi-maintenance.md @@ -0,0 +1,150 @@ +# OpenAPI Maintenance + +This document describes the Ruby client's architecture and the workflow for auditing it against the Mailinator OpenAPI specification. + +**OpenAPI specification:** [raw YAML](https://raw.githubusercontent.com/manybrain/mailinatordocs/main/openapi/mailinator-api.yaml) ([GitHub view](https://github.com/manybrain/mailinatordocs/blob/main/openapi/mailinator-api.yaml)) + +## Codebase Structure + +The structure under `lib/mailinator_client/` reflects Mailinator API resource groups. + +- `lib/mailinator_client.rb` loads all components and delegates module-level calls to a singleton `Client`. +- `lib/mailinator_client/client.rb` implements shared HTTP request behavior. +- `authenticators.rb`, `domains.rb`, `messages.rb`, `rules.rb`, `stats.rb`, and `webhooks.rb` contain resource wrappers. +- `utils.rb` normalizes input and query structures. +- `error.rb` defines `ResponseError`. +- `version.rb` defines gem version metadata used in user-agent headers. + +## Request and Response Conventions + +The client uses resource wrappers rather than per-operation request classes. Each resource method generally: + +1. Accepts a params hash and normalizes its keys with `Utils.symbolize_hash_keys`. +2. Validates required keys with `ArgumentError`. +3. Constructs a resource-relative `path`, `query`, and optional `body`. +4. Calls `@client.request(...)`. + +`Client#request` joins resource-relative paths such as `/domains/example.com/inboxes/test` to the base URL `https://api.mailinator.com/api/v2`. Resource methods must not include `/api/v2` or `/v2` in their paths. + +Requests use HTTParty with JSON headers and optional authorization. Responses with status codes of 400 or higher raise `MailinatorClient::ResponseError`, which exposes the HTTP `code`, API error `type`, and response message. + +The SDK generally returns parsed Hash and Array values rather than typed model objects. + +## Gap Analysis Workflow + +Use this workflow to identify differences between the OpenAPI specification and SDK coverage. + +### 1. Read the OpenAPI Specification + +Retrieve and parse the raw YAML linked above. For every `paths` entry, record: + +- HTTP method +- Full specification path +- `operationId` +- Tag +- Path and query parameters +- Request and response schemas relevant to implementation and tests + +### 2. Catalog the SDK + +For each resource wrapper under `lib/mailinator_client/`: + +1. Enumerate every public method that calls `@client.request(...)`. +2. Record its HTTP method and resource-relative path template. +3. Record the query parameters it sends. +4. Note methods marked as deprecated. +5. Map the resource file to the corresponding OpenAPI tag. + +### 3. Report Gaps + +Report these categories: + +#### Operations missing from the SDK + +List specification operations with no corresponding Ruby method. + +#### SDK operations missing from the specification + +List SDK methods whose resolved path and HTTP method have no specification entry. Note deprecated methods separately; flag other methods for clarification. + +#### URL construction mismatches + +The specification paths begin with `/api/v2/`, while SDK resource methods use paths relative to the client's `/api/v2` base URL. Flag: + +- A client base URL that does not end in `/api/v2`. +- Resource paths containing `/api/v2` or `/v2`, which would duplicate or bypass the client base path. +- Resolved SDK URLs that do not match the specification path. + +#### Parameter gaps + +For each matched operation, compare the SDK's path and query parameters with those declared by the specification. + +#### Domain-listing exception + +Treat `GET /api/v2/domains/{domain}/inboxes` as covered by `messages.fetch_inbox` with `inbox: "*"`. Do not report it as a missing SDK method. + +### 4. Propose a Plan + +Before changing code, present a plan containing: + +1. New methods, grouped by resource file. +2. URL-construction fixes. +3. Parameter additions. +4. Deprecated or undocumented methods, without removing them unless explicitly approved. +5. Response-shape expectations grounded in the specification. + +Wait for approval before implementing SDK coverage changes. + +### 5. Implement + +Follow the patterns already present in the matching resource wrapper: + +```ruby +def get_example(params = {}) + params = Utils.symbolize_hash_keys(params) + query_params = {} + headers = {} + body = nil + + raise ArgumentError, "domain is required" unless params.has_key?(:domain) + raise ArgumentError, "id is required" unless params.has_key?(:id) + + path = "/domains/#{params[:domain]}/examples/#{params[:id]}" + + @client.request( + method: :get, + path: path, + query: query_params, + headers: headers, + body: body + ) +end +``` + +Keep methods in the appropriate resource file, use snake_case method names, preserve the parameter naming conventions of that file, and document optional parameters. + +## Verification + +Run the complete suite: + +```sh +bundle exec rake test +``` + +Run a focused test when appropriate: + +```sh +bundle exec ruby -Itest test/messages_query_params_test.rb +``` + +Integration tests load local values from `.env` and skip when their required variables are missing. For each changed operation, verify that the resolved URL and query parameters exactly match the specification. Assertions must test response semantics and only fields guaranteed by the specification. + +## Additional Conventions + +| Convention | Detail | +|---|---| +| Version source | `MailinatorClient::VERSION` in `lib/mailinator_client/version.rb`, referenced by the gemspec and user-agent string | +| Authorization | `Client#request` adds the `Authorization` header when an auth token is provided | +| No-token requests | Supported for flows such as some webhook operations | +| Deprecation | Use Ruby/YARD comments and reflect the status in user-facing documentation | +| Entrypoint | `lib/mailinator_client.rb` requires resource and support files and delegates to the singleton client |