Skip to content

feat: add try-it-out execution - #74

Closed
Ivan-khodorov wants to merge 4 commits into
story/4-endpoint-detailsfrom
story/4-try-it-out
Closed

feat: add try-it-out execution#74
Ivan-khodorov wants to merge 4 commits into
story/4-endpoint-detailsfrom
story/4-try-it-out

Conversation

@Ivan-khodorov

@Ivan-khodorov Ivan-khodorov commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #66

Adds Try-it-out request execution to the Swagger Viewer on top of the endpoint details work from #72.

What Changed

  • Added server-side request execution route:

    • validates absolute HTTP/HTTPS target URLs
    • sends external API requests through the Next.js server to avoid browser CORS issues
    • returns response status, status text, headers, body, and request duration
    • keeps remote API 4xx/5xx responses in the response panel instead of treating them as app errors
  • Extended endpoint normalization with server URL extraction:

    • OpenAPI 3: servers[0].url
    • Swagger 2: schemes + host + basePath
  • Added Try-it-out UI in Swagger Viewer:

    • Server URL input
    • path/query/header/cookie parameter inputs
    • request body input
    • Execute button
    • response status, headers, and body display
  • Added EN/RU translations for Try-it-out labels.

  • Added tests for:

    • server URL extraction
    • request construction in the Viewer
    • response rendering
    • server route success, validation errors, and transport failure

Scope

This PR only implements Try-it-out request execution.

Not included in this PR:

  • cURL generation
  • copy-to-clipboard
  • request history
  • analytics
  • authenticated request persistence

These belong to follow-up tasks.

Verification

  • npm test
  • npm run lint
  • npm run build
  • git diff --check
  • Conflict check against story/4-endpoint-details
  • Conflict check against story/4-endpoint-list
  • Conflict check against develop

PR Dependency

This is a stacked PR and should be reviewed after #72.

Base branch: story/4-endpoint-details
Head branch: story/4-try-it-out

Notes

The implementation preserves the endpoint list/details changes from #69 and #72 and does not modify unrelated Editor/Auth/History behavior.

Summary by CodeRabbit

  • New Features

    • Added a per-endpoint “Try it out” workflow to execute documented APIs with query, path, header, cookie, and request-body inputs.
    • Displays response status, duration, headers, and body, including server URL awareness from the Swagger/OpenAPI definition.
    • Expanded English and Russian translations for the workflow and response/headers presentation.
  • Bug Fixes

    • Hardened safety checks for invalid, blocked (private/link-local), and redirected destinations.
    • Improved error handling and messaging for invalid requests, upstream failures, and request timeouts.
  • Tests

    • Added/expanded coverage for routing, redirects, blocking, failures, and timeout behavior.
    • Updated UI and endpoint extraction tests to include serverUrl.

@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
swagger-editor-app Ready Ready Preview, Comment Jul 11, 2026 2:36am

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5dc624c2-a86b-4454-9827-e5f1a80f8132

📥 Commits

Reviewing files that changed from the base of the PR and between c3f0108 and 78ee04f.

📒 Files selected for processing (2)
  • src/app/api/try-it-out/route.test.ts
  • src/app/api/try-it-out/route.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/app/api/try-it-out/route.ts
  • src/app/api/try-it-out/route.test.ts

📝 Walkthrough

Walkthrough

Adds Swagger endpoint server URL extraction, a validated /api/try-it-out proxy route, and a viewer flow for composing, executing, and displaying endpoint responses with localized UI text and tests.

Changes

Try It Out Flow

Layer / File(s) Summary
Endpoint server URL mapping
src/utils/swagger-editor/get-swagger-endpoints.ts, src/utils/swagger-editor/get-swagger-endpoints.test.ts
Extracted endpoints now include server URLs derived from OpenAPI servers or Swagger host, scheme, and base path values.
Validated request proxy
src/app/api/try-it-out/route.ts, src/app/api/try-it-out/route.test.ts
The POST route validates and sanitizes outbound request data, checks DNS-resolved destinations, handles redirects and timeouts, proxies requests, returns response metadata, and maps failures to error responses.
Viewer execution and response UI
src/components/swagger-viewer/swagger-viewer.tsx, src/components/swagger-viewer/swagger-viewer.test.tsx, src/i18n/messages/*.json
The viewer composes endpoint parameters, submits try-it-out requests, renders responses, integrates toast handling, and adds English and Russian translations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SwaggerViewer
  participant TryItOutRoute
  participant DNSResolver
  participant NodeHttpClient
  User->>SwaggerViewer: Enter parameters and execute endpoint
  SwaggerViewer->>TryItOutRoute: POST composed request
  TryItOutRoute->>DNSResolver: Validate destination address
  DNSResolver-->>TryItOutRoute: Return resolved public address
  TryItOutRoute->>NodeHttpClient: Proxy validated request
  NodeHttpClient-->>TryItOutRoute: Return response
  TryItOutRoute-->>SwaggerViewer: Return response envelope
  SwaggerViewer-->>User: Display status, headers, duration, and body
Loading

Possibly related PRs

Suggested reviewers: YuliaDemir, lexarudak

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding try-it-out execution.
Linked Issues check ✅ Passed The PR implements the Try-it-out feature from #66 with request execution, response display, server URL extraction, and tests.
Out of Scope Changes check ✅ Passed The changes stay focused on the Try-it-out workflow, its supporting URL extraction, translations, and tests.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch story/4-try-it-out

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (3)
src/components/swagger-viewer/swagger-viewer.tsx (1)

102-166: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting Try-It-Out logic into its own module.

swagger-viewer.tsx is accumulating several independent concerns in one file: schema-list rendering, EndpointPanel, path/URL-building helpers (applyPathParameters, buildTryItOutRequest, joinUrl, getParameterKey), and the entire TryItOutPanel/TryItOutResponseDetails implementation. Splitting the try-it-out pieces into a dedicated file (e.g. try-it-out-panel.tsx) would keep this file cohesive and easier to navigate as it continues to grow across this stacked PR series.

As per path instructions, src/**/*: "Flag code smells: God-objects, duplicate code chunks, unused files or exports."

Also applies to: 201-260, 295-301, 389-548

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/swagger-viewer/swagger-viewer.tsx` around lines 102 - 166,
Extract the Try-It-Out functionality from swagger-viewer.tsx into a dedicated
module, including applyPathParameters, buildTryItOutRequest, joinUrl,
getParameterKey, TryItOutPanel, and TryItOutResponseDetails. Update
EndpointPanel and any other callers to import the moved symbols, preserving
existing behavior and exports while leaving schema-list rendering and unrelated
Swagger viewer logic in swagger-viewer.tsx.

Source: Path instructions

src/components/swagger-viewer/swagger-viewer.test.tsx (1)

126-222: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing test for remote 4xx/5xx displayed in the response panel.

This new test only covers a successful (200) proxied response. Given the explicit product requirement that remote 4xx/5xx must render inside the response section rather than as an error toast, it'd be worth adding a case where the mocked /api/try-it-out response contains e.g. status: 404 and asserting it renders via TryItOutResponseDetails (red chip) without triggering showToast.

As per path instructions: "HTTP 4xx and 5xx status codes returned from user-executed API requests (Try-It-Out feature) must be displayed within the response section of the UI, not surfaced as application-level error toasts."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/swagger-viewer/swagger-viewer.test.tsx` around lines 126 -
222, The SwaggerViewer tests lack coverage for proxied remote 4xx/5xx responses.
Add a test alongside the existing endpoint execution test that mocks
/api/try-it-out with a non-OK response such as status 404, executes the
endpoint, and asserts TryItOutResponseDetails renders the status and response
body in the response section with the red status treatment; also spy on
showToast and verify it is not called.

Source: Path instructions

src/utils/swagger-editor/get-swagger-endpoints.ts (1)

133-156: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

getDefaultServerUrl doesn't validate absolute URLs or respect path/operation-level overrides.

Two spec gaps here:

  1. OpenAPI 3 servers[].url may be a relative reference (resolved against the document's hosting location), which is valid per spec. This function returns it as-is; downstream buildTryItOutRequest/TryItOutPanel does new URL(joinUrl(serverUrl, path)), which throws for a relative string like /v2, causing a generic "invalid URL" error for specs that legitimately use relative servers.
  2. Per the OpenAPI spec, servers can also be declared at the Path Item and Operation level, and those levels override the document-level servers. This implementation only reads schema.servers (document root), so any endpoint with a path/operation-level server override will incorrectly show the document-level (or wrong) URL.

Both are edge cases with a safe fallback (the server URL field is user-editable), so functionality isn't fully broken, but this can silently mislead users composing requests.

♻️ Suggested guard for relative URLs (path/operation overrides need broader changes to plumb per-operation server data through)
   if (Array.isArray(servers)) {
     const firstServer = servers.find((server) => isRecord(server) && getString(server.url));

     if (isRecord(firstServer)) {
-      return getString(firstServer.url);
+      const url = getString(firstServer.url);
+
+      try {
+        return new URL(url).toString();
+      } catch {
+        return ''; // relative server URL - fall back to manual entry
+      }
     }
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/swagger-editor/get-swagger-endpoints.ts` around lines 133 - 156,
Update getDefaultServerUrl to handle relative OpenAPI 3 server URLs safely:
validate or resolve servers[].url against an available document base URL before
returning it, with a safe fallback for unresolved relative references so
downstream buildTryItOutRequest and TryItOutPanel do not pass invalid input to
new URL. Also trace endpoint construction to propagate Path Item and
Operation-level servers, ensuring those overrides take precedence over
document-level servers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/api/try-it-out/route.ts`:
- Around line 45-51: Replace the hardcoded English error messages in the
try-it-out route’s error responses with stable error codes, including the
invalid payload, missing URL/method, and execution failure cases. Update the
client error handling in swagger-viewer.tsx to map those codes through the
appropriate i18n keys, while retaining a translated fallback for unknown errors.
- Around line 36-43: Add an AbortController-based timeout around the fetch in
the route handler, passing its signal to fetch and clearing the timeout after
completion; handle timeout failures consistently with other request errors. Also
tighten the existing URL validation to reject arbitrary http(s) destinations and
permit only the intended safe targets.
- Around line 22-54: Update POST and its URL/request helpers, including getUrl,
to prevent SSRF: resolve the initial hostname and reject loopback, private,
link-local, and other non-public IP ranges, use fetch with redirect set to
manual, and validate every Location redirect target before following it.
Preserve existing validation and return a suitable client error for blocked
destinations.

In `@src/components/swagger-viewer/swagger-viewer.tsx`:
- Around line 429-434: Replace the raw string handling in the swagger viewer’s
request failure logic with a coordinated error-code flow: update the route
handler to return a stable error code instead of localized text, then map that
code through the client’s translation function before calling setError in the
!result.ok branch. Preserve a translated fallback such as t('tryItOutFailed')
for unknown or malformed errors, and update related error handling consistently
on both sides.

In `@src/i18n/messages/ru.json`:
- Line 50: Translate the tryItOutTitle value in the Russian messages resource
instead of leaving the English text “Try it out”; use a natural Russian
equivalent consistent with the surrounding translations.

---

Nitpick comments:
In `@src/components/swagger-viewer/swagger-viewer.test.tsx`:
- Around line 126-222: The SwaggerViewer tests lack coverage for proxied remote
4xx/5xx responses. Add a test alongside the existing endpoint execution test
that mocks /api/try-it-out with a non-OK response such as status 404, executes
the endpoint, and asserts TryItOutResponseDetails renders the status and
response body in the response section with the red status treatment; also spy on
showToast and verify it is not called.

In `@src/components/swagger-viewer/swagger-viewer.tsx`:
- Around line 102-166: Extract the Try-It-Out functionality from
swagger-viewer.tsx into a dedicated module, including applyPathParameters,
buildTryItOutRequest, joinUrl, getParameterKey, TryItOutPanel, and
TryItOutResponseDetails. Update EndpointPanel and any other callers to import
the moved symbols, preserving existing behavior and exports while leaving
schema-list rendering and unrelated Swagger viewer logic in swagger-viewer.tsx.

In `@src/utils/swagger-editor/get-swagger-endpoints.ts`:
- Around line 133-156: Update getDefaultServerUrl to handle relative OpenAPI 3
server URLs safely: validate or resolve servers[].url against an available
document base URL before returning it, with a safe fallback for unresolved
relative references so downstream buildTryItOutRequest and TryItOutPanel do not
pass invalid input to new URL. Also trace endpoint construction to propagate
Path Item and Operation-level servers, ensuring those overrides take precedence
over document-level servers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b40644e-ab2b-418a-847b-7684d09fcb76

📥 Commits

Reviewing files that changed from the base of the PR and between 66ca43a and 6e266d0.

📒 Files selected for processing (8)
  • src/app/api/try-it-out/route.test.ts
  • src/app/api/try-it-out/route.ts
  • src/components/swagger-viewer/swagger-viewer.test.tsx
  • src/components/swagger-viewer/swagger-viewer.tsx
  • src/i18n/messages/en.json
  • src/i18n/messages/ru.json
  • src/utils/swagger-editor/get-swagger-endpoints.test.ts
  • src/utils/swagger-editor/get-swagger-endpoints.ts

Comment thread src/app/api/try-it-out/route.ts
Comment thread src/app/api/try-it-out/route.ts Outdated
Comment thread src/app/api/try-it-out/route.ts
Comment thread src/components/swagger-viewer/swagger-viewer.tsx
Comment thread src/i18n/messages/ru.json Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/app/api/try-it-out/route.test.ts (1)

102-121: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Good coverage for blocking private destinations and blocked redirect targets. Consider adding an IPv6 case (e.g. a non-fe80::-literal link-local address) once the classification gap in isPublicIp is fixed, to lock in the corrected behavior.

Also applies to: 169-191

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/try-it-out/route.test.ts` around lines 102 - 121, Add an IPv6
link-local destination test alongside the existing private-destination and
blocked-redirect tests in the try-it-out route test suite. Use a non-fe80:: IPv6
literal that should be classified as private, assert POST returns status 400
with errorCode "blockedUrl", and verify fetch is not called; include the
corresponding redirect-target coverage if applicable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/api/try-it-out/route.ts`:
- Around line 220-236: Expand the IPv6 link-local validation in the ipVersion
=== 6 branch to cover the entire fe80::/10 range, not only addresses starting
with “fe80:”. Parse the first IPv6 hextet or use an equivalent range check and
reject values from 0xfe80 through 0xfebf, while preserving the existing handling
in the address normalization and isPublicIp logic.
- Around line 74-108: Replace the separate DNS preflight in assertPublicUrl and
unpinned fetchWithTimeout usage in fetchValidatedUrl with an outbound
connector/dispatcher that resolves the hostname once, validates every resolved
address with isPublicIp, and pins that validated address for the actual
connection; preserve protocol and redirect checks and apply the same protection
on each redirect.

---

Nitpick comments:
In `@src/app/api/try-it-out/route.test.ts`:
- Around line 102-121: Add an IPv6 link-local destination test alongside the
existing private-destination and blocked-redirect tests in the try-it-out route
test suite. Use a non-fe80:: IPv6 literal that should be classified as private,
assert POST returns status 400 with errorCode "blockedUrl", and verify fetch is
not called; include the corresponding redirect-target coverage if applicable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d26b721e-cba7-416b-b3d8-7b70065af58d

📥 Commits

Reviewing files that changed from the base of the PR and between 6e266d0 and 3d2d770.

📒 Files selected for processing (6)
  • src/app/api/try-it-out/route.test.ts
  • src/app/api/try-it-out/route.ts
  • src/components/swagger-viewer/swagger-viewer.test.tsx
  • src/components/swagger-viewer/swagger-viewer.tsx
  • src/i18n/messages/en.json
  • src/i18n/messages/ru.json
✅ Files skipped from review due to trivial changes (2)
  • src/i18n/messages/ru.json
  • src/i18n/messages/en.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/swagger-viewer/swagger-viewer.tsx

Comment thread src/app/api/try-it-out/route.ts Outdated
Comment thread src/app/api/try-it-out/route.ts
@AlyaEngineer AlyaEngineer added the feature-4-viewer Feature 4: Swagger Viewer label Jul 10, 2026
@AlyaEngineer AlyaEngineer linked an issue Jul 10, 2026 that may be closed by this pull request

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/app/api/try-it-out/route.ts (2)

91-104: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Redirects re-send Authorization/Cookie to the redirect target, including cross-host hops.

init (with the user's original headers) is reused unchanged on every redirect hop. If the initial origin responds with a 302 to a different host, the same Authorization/Cookie headers are replayed to that host. Each hop is IP-validated, but a redirect controlled by the target can still exfiltrate credentials the user entered for their own API. Standard clients strip credential headers on cross-origin redirects.

Consider dropping sensitive headers when new URL(location, currentUrl).host !== currentUrl.host.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/try-it-out/route.ts` around lines 91 - 104, fetchValidatedUrl
must not forward credentials across origins. When resolving each redirect in
fetchValidatedUrl, compare the destination URL’s host with currentUrl.host; if
they differ, create redirect request options with Authorization and Cookie
headers removed before calling requestWithPinnedIp, while preserving other
headers and same-host credentials.

265-277: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Response body is buffered without a size limit.

Buffer.concat(chunks) accumulates the entire upstream body in memory. A large or malicious origin can grow this unbounded within the 15s window and exhaust process memory on this shared route. Consider tracking cumulative length and destroying the request once a cap is exceeded.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/try-it-out/route.ts` around lines 265 - 277, Limit response
buffering in the response handling around the `response.on('data')` and
`response.on('end')` callbacks. Track cumulative chunk size, enforce a
reasonable maximum body size, and destroy or abort the request and reject the
promise when the cap is exceeded; only call `Buffer.concat(chunks)` after
confirming the limit was not exceeded.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/api/try-it-out/route.ts`:
- Around line 263-278: Add an error listener to the response stream in the
Promise within the try-it-out request handler, using response.on('error',
reject) alongside the existing data and end handlers. Keep
requestMessage.on('error') for client-side failures so both request and response
stream errors settle the Promise.

---

Nitpick comments:
In `@src/app/api/try-it-out/route.ts`:
- Around line 91-104: fetchValidatedUrl must not forward credentials across
origins. When resolving each redirect in fetchValidatedUrl, compare the
destination URL’s host with currentUrl.host; if they differ, create redirect
request options with Authorization and Cookie headers removed before calling
requestWithPinnedIp, while preserving other headers and same-host credentials.
- Around line 265-277: Limit response buffering in the response handling around
the `response.on('data')` and `response.on('end')` callbacks. Track cumulative
chunk size, enforce a reasonable maximum body size, and destroy or abort the
request and reject the promise when the cap is exceeded; only call
`Buffer.concat(chunks)` after confirming the limit was not exceeded.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d6998ebf-0e31-4fa6-ae77-0809c43a57d4

📥 Commits

Reviewing files that changed from the base of the PR and between 3d2d770 and c3f0108.

📒 Files selected for processing (2)
  • src/app/api/try-it-out/route.test.ts
  • src/app/api/try-it-out/route.ts

Comment thread src/app/api/try-it-out/route.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature-4-viewer Feature 4: Swagger Viewer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Try-it-out

2 participants