Skip to content

feat: allow duplicate http names by method#7949

Open
levcoder wants to merge 1 commit into
usebruno:mainfrom
levcoder:method-aware-http-filenames
Open

feat: allow duplicate http names by method#7949
levcoder wants to merge 1 commit into
usebruno:mainfrom
levcoder:method-aware-http-filenames

Conversation

@levcoder
Copy link
Copy Markdown

@levcoder levcoder commented May 7, 2026

Description

Allows HTTP requests in the same collection folder to share the same visible display name when they use different HTTP methods.

This addresses related requests/discussions:

This keeps request metadata names unchanged (meta.name / info.name) and makes the filesystem filename method-aware for auto-generated HTTP request files, for example:

  • GET /projects -> GET projects.bru
  • POST /projects -> POST projects.bru
  • PUT /projects/{id} -> PUT projects-{id}.bru

Screenshot

изображение

The change preserves filesystem collision protection and respects manually edited filesystem names. It also updates OpenAPI, Swagger 2, and Postman import paths so duplicate operation/request names with different methods keep clean display names while receiving unique method-aware filenames.

Contribution Checklist:

  • I've used AI significantly to create this pull request
  • The pull request only addresses one issue or adds one feature.
  • The pull request does not introduce any breaking changes
  • I have added screenshots or gifs to help explain the change if applicable.
  • I have read the contribution guidelines.
  • Create an issue and link to the pull request.

Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.

Publishing to New Package Managers

Please see here for more information.

Summary by CodeRabbit

  • New Features

    • Request filenames now auto-generate intelligently based on HTTP method and request name
    • Automatic filename updates when renaming requests, with manual editing support to disable auto-generation
  • Improvements

    • Enhanced request deduplication prevents filename collisions when importing from Postman, OpenAPI, and Swagger collections
    • Improved naming consistency across collection imports, folder cloning, and synchronization operations ensures unique, readable filenames

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Review Change Stack

Walkthrough

This PR enables requests with identical names but different HTTP methods within a single folder. It introduces HTTP method-aware request filename utilities (e.g., "GET projects", "POST projects") across app, CLI, converters, and Electron packages; integrates auto-filename generation in request creation/rename UI with manual-edit detection; and updates collection import and API specification converters to use per-folder unique filename tracking.

Changes

HTTP Method-Aware Request Filenames

Layer / File(s) Summary
Filename Utilities
packages/bruno-app/src/utils/common/requestFilename.js, packages/bruno-cli/src/utils/request-filename.js, packages/bruno-converters/src/utils/request-filename.js, packages/bruno-electron/src/utils/request-filename.js
New exported functions: getHttpRequestFilenameBase(requestName, method) produces sanitized "METHOD name" bases; getRequestFilenameBase({requestName, requestMethod, requestType}) routes HTTP vs. generic naming; normalizeRequestFilename(filename, format) ensures consistent .bru/.yml extensions; getUniqueRequestFilename(item, format, checkExists) appends numeric counters on collisions.
UI Auto-Generation
packages/bruno-app/src/components/Sidebar/Collections/.../RenameCollectionItem/index.js, packages/bruno-app/src/components/Sidebar/NewRequest/index.js
RenameCollectionItem imports getHttpRequestFilenameBase, computes expected filename from name + method, and auto-updates on name changes until user manually edits filename. NewRequest adds isFilenameManuallyEdited state flag; auto-generates filename on request type/method/name changes via setAutoFilename helper; switches to manual filename on direct edit; selects between auto-generated and user-entered filename during cURL submission.
Collection/Import Filename Generation
packages/bruno-cli/src/utils/collection.js, packages/bruno-electron/src/ipc/collection.js, packages/bruno-electron/src/ipc/openapi-sync.js, packages/bruno-electron/src/utils/collection-import.js
Collection import/export flows now use getUniqueRequestFilename with per-folder Set tracking and filesystem checks to generate collision-free filenames during processCollectionItems, renderer:import-collection, renderer:clone-folder, and renderer:apply-openapi-sync/renderer:add-missing-endpoints.
OpenAPI/Swagger/Postman Converters
packages/bruno-converters/src/openapi/{openapi-common,openapi-to-bruno}.js, packages/bruno-converters/src/openapi/swagger2-to-bruno.js, packages/bruno-converters/src/postman/postman-to-bruno.js
Replace single shared uniqueness tracking with per-folder usedFilenames sets; remove in-function operation name deduplication; assign filename via getUniqueHttpRequestFilename(operationName, request.method, usedFilenames), preserving operation name for display while using method-prefixed filename for storage.
Tests and Validation
packages/bruno-app/src/utils/common/requestFilename.spec.js, packages/bruno-electron/src/utils/request-filename.test.js, packages/bruno-converters/tests/openapi/**, packages/bruno-converters/tests/postman/**
New test suites verify getHttpRequestFilenameBase produces method-aware filenames, normalizeRequestFilename remaps extensions, getUniqueRequestFilename appends counters on collision, and OpenAPI/Swagger/Postman converters produce distinct filenames for same-named requests with different HTTP methods.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • usebruno/bruno#7622: Shares OpenAPI conversion pipeline modifications and HTTP-method-aware filename helper logic.
  • usebruno/bruno#7489: Overlaps in Electron openapi-sync.js IPC filename generation flows; may require merge sequencing.

Suggested labels

size/XXL, feat/api-request-management

Suggested reviewers

  • helloanoop
  • lohit-bruno
  • naman-bruno
  • bijin-bruno

Poem

🚀 With methods now distinct in filing scheme,
GET and POST no longer clash or scream—
Each endpoint finds its rightful name,
From OpenAPI to CLI, all the same.
Requests bloom where sameness reigned before! 🌸

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature: enabling duplicate HTTP request names by differentiating them via HTTP methods.
Linked Issues check ✅ Passed Code changes implement all core requirements from issue #3502: method-aware filenames for duplicate request names, preserved display metadata, and collision-free filename generation.
Out of Scope Changes check ✅ Passed All changes directly support method-aware filename generation across UI components and import converters; no extraneous modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/bruno-electron/src/ipc/collection.js (1)

1344-1350: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

folderContent.name = item.name is a no-op — stringifyFolder returns a string.

stringifyFolder returns a serialized string; assigning a property to it does nothing in JavaScript. The intent was presumably to override the folder name before writing, but it has no effect — the file is written with whatever was already in item.root.

If the name in item.root.meta.name is always consistent with item.name (expected during clone), this is harmless dead code. If not, the folder file will silently contain a stale name.

🐛 Proposed fix
 if (item.root) {
+  if (item.root.meta) item.root.meta.name = item.name;
   const folderContent = await stringifyFolder(item.root, { format });
-  folderContent.name = item.name;
   if (folderContent) {
     const folderFilePath = path.join(folderPath, `folder.${format}`);
     safeWriteFileSync(folderFilePath, folderContent);
   }
 }
🤖 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 `@packages/bruno-electron/src/ipc/collection.js` around lines 1344 - 1350, The
code assigns to folderContent.name but stringifyFolder(item.root, { format })
returns a string, so folderContent.name is a no-op; update the logic to mutate
the folder data before serialization: set the desired name on the source object
(e.g., item.root.meta.name = item.name) or pass an override name into
stringifyFolder if it supports options, then call stringifyFolder and write the
returned string with safeWriteFileSync to folderFilePath; ensure you update the
call site around stringifyFolder, item.root, folderFilePath, and
safeWriteFileSync so the serialized file contains the correct name.
🧹 Nitpick comments (6)
packages/bruno-app/src/components/Sidebar/NewRequest/index.js (1)

276-287: ⚡ Quick win

Add behavior tests for auto-filename/manual-override transitions in New Request.

This flow now has multiple conditional update paths (name/method/requestType/cURL parsing/manual-edit), and it should be locked with focused tests.

As per coding guidelines, "Add tests for any new functionality or meaningful changes... Focus on testing behaviour that is critical, complex, or likely to break."

Also applies to: 295-350

🤖 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 `@packages/bruno-app/src/components/Sidebar/NewRequest/index.js` around lines
276 - 287, Add behavior tests that cover setAutoFilename's conditional filename
updates and manual-override behavior: write unit tests for the New Request
component invoking setAutoFilename (or simulating user interactions that call
it) to verify that when isFilenameManuallyEdited is false the filename field
(formik.values.filename / formik.setFieldValue) updates for changes in
requestName, requestMethod, and requestType (including when requestType ===
'from-curl' using curlRequestTypeDetected fallback), and that when
isFilenameManuallyEdited is true the filename does not change; also add tests
for transitions (toggle manual edit on/off) to ensure auto-updates resume after
manual-override is cleared and that getRequestFilenameBase is used to compute
the value.
packages/bruno-converters/src/postman/postman-to-bruno.js (1)

367-450: 💤 Low value

LGTM on the dedup wiring. 🧵

usedRequestFilenames is created inside importPostmanV2CollectionItem, which is invoked recursively per folder (line 419) — so each folder ends up with its own filename namespace, which lines up nicely with the on-disk folder structure.

Tiny nit on lines 441–442: const requestName = baseRequestName; is a leftover alias from the prior dedup logic and can be folded into a single declaration. Not blocking.

♻️ Optional simplification
-      const baseRequestName = i.name || 'Untitled Request';
-      const requestName = baseRequestName;
+      const requestName = i.name || 'Untitled Request';
       const filename = getUniqueHttpRequestFilename(requestName, method, usedRequestFilenames);
🤖 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 `@packages/bruno-converters/src/postman/postman-to-bruno.js` around lines 367 -
450, The code contains a redundant alias where baseRequestName is declared then
immediately copied into requestName inside importPostmanV2CollectionItem;
collapse these into a single declaration by removing baseRequestName and
declaring requestName directly from i.name (e.g., replace the two declarations
with one that sets requestName = i.name || 'Untitled Request') and keep using
requestName for filename generation and the brunoRequestItem.
packages/bruno-electron/src/utils/request-filename.js (1)

43-61: 💤 Low value

No upper bound on the collision-avoidance counter loop.

If checkExists were to behave unexpectedly (e.g., always returns true), the loop has no exit. Practically this won't happen with well-formed callers, but a max guard keeps the contract explicit.

♻️ Proposed guard
+const MAX_COUNTER = 9999;
 let counter = 1;
 let uniqueFilename = `${baseName} ${counter}.${extension}`;

-while (checkExists(uniqueFilename)) {
+while (checkExists(uniqueFilename) && counter <= MAX_COUNTER) {
   counter++;
   uniqueFilename = `${baseName} ${counter}.${extension}`;
 }
🤖 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 `@packages/bruno-electron/src/utils/request-filename.js` around lines 43 - 61,
The getUniqueRequestFilename function currently loops until checkExists returns
false with no upper bound; add a max-attempts guard (e.g., MAX_ATTEMPTS
constant) and break/throw after exceeding it to avoid infinite loops if
checkExists misbehaves. Update the loop that increments counter and builds
uniqueFilename in getUniqueRequestFilename to stop once counter > MAX_ATTEMPTS
and either throw a descriptive error (including baseName and extension) or
return a safe fallback filename, ensuring callers can handle the failure.
packages/bruno-electron/src/utils/request-filename.test.js (1)

1-25: ⚡ Quick win

getRequestFilename has zero test coverage.

The three branching paths in getRequestFilename (item.filename present, type === 'http-request', and the fallback for graphql/grpc/ws) are untested, and the no-collision base case for getUniqueRequestFilename is also missing.

As per coding guidelines, new functionality should have corresponding tests covering the happy path and problematic paths.

✅ Suggested additions
+it('getRequestFilename uses item.filename when present', () => {
+  const item = { filename: 'custom name.bru', type: 'http-request', name: 'ignored', request: { method: 'GET' } };
+  const { getRequestFilename } = require('./request-filename');
+  expect(getRequestFilename(item, 'bru')).toBe('custom name.bru');
+});
+
+it('getRequestFilename uses method prefix for http-request', () => {
+  const item = { type: 'http-request', name: '/projects', request: { method: 'POST' } };
+  const { getRequestFilename } = require('./request-filename');
+  expect(getRequestFilename(item, 'bru')).toBe('POST projects.bru');
+});
+
+it('getRequestFilename uses plain name for non-http types', () => {
+  const item = { type: 'graphql-request', name: 'My Query' };
+  const { getRequestFilename } = require('./request-filename');
+  expect(getRequestFilename(item, 'bru')).toBe('My Query.bru');
+});
+
+it('returns filename unchanged when no collision', () => {
+  const item = { type: 'http-request', name: '/users', request: { method: 'GET' } };
+  expect(getUniqueRequestFilename(item, 'bru', () => false)).toBe('GET users.bru');
+});
+
+it('increments counter past 1 when multiple collisions exist', () => {
+  const existing = new Set(['GET users.bru', 'GET users 1.bru']);
+  const item = { type: 'http-request', name: '/users', request: { method: 'GET' } };
+  expect(getUniqueRequestFilename(item, 'bru', (f) => existing.has(f))).toBe('GET users 2.bru');
+});
🤖 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 `@packages/bruno-electron/src/utils/request-filename.test.js` around lines 1 -
25, Add unit tests in request-filename.test.js to cover all branches of
getRequestFilename: (1) when item.filename is provided, assert it returns that
filename, (2) when item.type === 'http-request' verify it delegates to
getHttpRequestFilenameBase and respects method/name, and (3) the fallback branch
for graphql/grpc/ws items (e.g., type !== 'http-request') to ensure the
appropriate normalized filename is returned; also add the missing no-collision
base case for getUniqueRequestFilename to assert that when the collision checker
returns false the filename is returned without a counter, and include tests for
normalizeRequestFilename behavior where relevant.
packages/bruno-electron/src/ipc/openapi-sync.js (1)

1238-1247: ⚡ Quick win

Three getUniqueRequestFilename call sites lack in-memory filenamesInFolder tracking — inconsistent with every other call site in this PR.

All three locations (reset mode at line 1245, sync add-new at line 1376, add-missing at line 1622) rely solely on fs.existsSync. This works today because each write is awaited before the next check, but it diverges from the filenamesInFolder.has || fs.existsSync pattern used in collection-import.js and collection.js. Two spec endpoints that sanitize to the same filename within the same folder would collide in the window between getUniqueRequestFilename returning and writeFile completing.

♻️ Example fix pattern (apply identically at all 3 sites)
+const filenamesInFolder = new Map(); // keyed by targetFolder path
 for (const [, specItem] of specItemsMap) {
   let targetFolder = collectionPath;
   if (specItem.folderName && groupBy === 'tags') {
     targetFolder = await ensureTagFolder(collectionPath, specItem.folderName, format);
   }
+  if (!filenamesInFolder.has(targetFolder)) filenamesInFolder.set(targetFolder, new Set());
+  const folderSet = filenamesInFolder.get(targetFolder);

   const requestContent = await stringifyRequestViaWorker(specItem, { format });
-  const sanitizedFilename = getUniqueRequestFilename(specItem, format, (filename) => fs.existsSync(path.join(targetFolder, filename)));
+  const sanitizedFilename = getUniqueRequestFilename(specItem, format, (filename) => folderSet.has(filename) || fs.existsSync(path.join(targetFolder, filename)));
+  folderSet.add(sanitizedFilename);
   await writeFile(path.join(targetFolder, sanitizedFilename), requestContent);
 }

Also applies to: 1368-1378, 1619-1624

🤖 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 `@packages/bruno-electron/src/ipc/openapi-sync.js` around lines 1238 - 1247,
The three call sites that invoke getUniqueRequestFilename (inside the loop that
may call ensureTagFolder, and the other two similar sites) must use an in-memory
filenamesInFolder Set like other files in this PR: initialize or reuse a Set for
the targetFolder, pass a checker callback to getUniqueRequestFilename that
returns filenamesInFolder.has(filename) || fs.existsSync(path.join(targetFolder,
filename)), then immediately add the returned sanitized filename to
filenamesInFolder before awaiting writeFile; apply this same pattern to the
reset mode, sync add-new, and add-missing loops so concurrent/same-run
collisions are prevented.
packages/bruno-cli/src/utils/request-filename.js (1)

1-68: ⚡ Quick win

Duplicate file and inconsistent sanitizeName implementations need consolidation.

packages/bruno-cli/src/utils/request-filename.js and packages/bruno-electron/src/utils/request-filename.js are identical; any future fix requires manual synchronization across both packages.

The local sanitizeName in request-filename.js differs from the one in filesystem.js: the CLI filesystem version uses /^[.\s-]+/ to remove leading dots, hyphens, and spaces, while request-filename versions use /^[\s\-]+/ to remove only leading spaces and hyphens. This inconsistency means folder names and request filenames could be sanitized by different rules, creating potential naming conflicts or unexpected behavior across the CLI and Electron packages.

Consider consolidating both request-filename.js files and aligning sanitizeName implementations across the codebase to use a single, consistent function.

🤖 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 `@packages/bruno-cli/src/utils/request-filename.js` around lines 1 - 68, Two
problems: the sanitizeName implementation in request-filename.js diverges from
filesystem.js and the same request-filename logic is duplicated in two packages;
consolidate and align them. Create a single shared sanitize utility (exporting
sanitizeName) and update getHttpRequestFilenameBase, normalizeRequestFilename,
getRequestFilename and getUniqueRequestFilename to import and use that shared
sanitizeName; ensure the canonical sanitizeName uses the filesystem.js pattern
(/^[.\s-]+/ for leading chars and the same trailing removal) so both CLI and
Electron use identical sanitization, and replace the duplicated
request-filename.js copies to import the shared module.
🤖 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 `@packages/bruno-app/src/components/Sidebar/NewRequest/index.js`:
- Around line 77-79: curlRequestTypeChange currently only calls
setCurlRequestTypeDetected, leaving an auto-generated filename stale when the
user toggles between HTTP and GraphQL; update curlRequestTypeChange to also
detect if the filename is in auto-mode (the auto flag, e.g., isFilenameAuto or
filenameAuto) and when true compute the new auto filename (use the existing
helper that derives the auto name or replicate its logic) and call setFilename
with that value (and ensure the auto flag remains true via setIsFilenameAuto if
applicable); apply the same change pattern to the other handler(s) referenced
around lines 603-615 so toggling the cURL type always resyncs the auto filename.

In `@packages/bruno-app/src/utils/common/requestFilename.js`:
- Around line 20-23: The code lowercases the extension into the variable
extension and then passes that lowercased value to path.basename, which breaks
when the file's actual suffix has different casing; change the logic to keep a
lowercased copy for the includes check but obtain the original-case suffix (e.g.
const origExt = path.extname(filename)) and pass origExt to path.basename when
stripping (use extensionLower = origExt.toLowerCase() for the includes check and
call path.basename(filename, origExt) when creating baseName), ensuring
filename, extension (origExt), and baseName references are updated accordingly.

---

Outside diff comments:
In `@packages/bruno-electron/src/ipc/collection.js`:
- Around line 1344-1350: The code assigns to folderContent.name but
stringifyFolder(item.root, { format }) returns a string, so folderContent.name
is a no-op; update the logic to mutate the folder data before serialization: set
the desired name on the source object (e.g., item.root.meta.name = item.name) or
pass an override name into stringifyFolder if it supports options, then call
stringifyFolder and write the returned string with safeWriteFileSync to
folderFilePath; ensure you update the call site around stringifyFolder,
item.root, folderFilePath, and safeWriteFileSync so the serialized file contains
the correct name.

---

Nitpick comments:
In `@packages/bruno-app/src/components/Sidebar/NewRequest/index.js`:
- Around line 276-287: Add behavior tests that cover setAutoFilename's
conditional filename updates and manual-override behavior: write unit tests for
the New Request component invoking setAutoFilename (or simulating user
interactions that call it) to verify that when isFilenameManuallyEdited is false
the filename field (formik.values.filename / formik.setFieldValue) updates for
changes in requestName, requestMethod, and requestType (including when
requestType === 'from-curl' using curlRequestTypeDetected fallback), and that
when isFilenameManuallyEdited is true the filename does not change; also add
tests for transitions (toggle manual edit on/off) to ensure auto-updates resume
after manual-override is cleared and that getRequestFilenameBase is used to
compute the value.

In `@packages/bruno-cli/src/utils/request-filename.js`:
- Around line 1-68: Two problems: the sanitizeName implementation in
request-filename.js diverges from filesystem.js and the same request-filename
logic is duplicated in two packages; consolidate and align them. Create a single
shared sanitize utility (exporting sanitizeName) and update
getHttpRequestFilenameBase, normalizeRequestFilename, getRequestFilename and
getUniqueRequestFilename to import and use that shared sanitizeName; ensure the
canonical sanitizeName uses the filesystem.js pattern (/^[.\s-]+/ for leading
chars and the same trailing removal) so both CLI and Electron use identical
sanitization, and replace the duplicated request-filename.js copies to import
the shared module.

In `@packages/bruno-converters/src/postman/postman-to-bruno.js`:
- Around line 367-450: The code contains a redundant alias where baseRequestName
is declared then immediately copied into requestName inside
importPostmanV2CollectionItem; collapse these into a single declaration by
removing baseRequestName and declaring requestName directly from i.name (e.g.,
replace the two declarations with one that sets requestName = i.name ||
'Untitled Request') and keep using requestName for filename generation and the
brunoRequestItem.

In `@packages/bruno-electron/src/ipc/openapi-sync.js`:
- Around line 1238-1247: The three call sites that invoke
getUniqueRequestFilename (inside the loop that may call ensureTagFolder, and the
other two similar sites) must use an in-memory filenamesInFolder Set like other
files in this PR: initialize or reuse a Set for the targetFolder, pass a checker
callback to getUniqueRequestFilename that returns
filenamesInFolder.has(filename) || fs.existsSync(path.join(targetFolder,
filename)), then immediately add the returned sanitized filename to
filenamesInFolder before awaiting writeFile; apply this same pattern to the
reset mode, sync add-new, and add-missing loops so concurrent/same-run
collisions are prevented.

In `@packages/bruno-electron/src/utils/request-filename.js`:
- Around line 43-61: The getUniqueRequestFilename function currently loops until
checkExists returns false with no upper bound; add a max-attempts guard (e.g.,
MAX_ATTEMPTS constant) and break/throw after exceeding it to avoid infinite
loops if checkExists misbehaves. Update the loop that increments counter and
builds uniqueFilename in getUniqueRequestFilename to stop once counter >
MAX_ATTEMPTS and either throw a descriptive error (including baseName and
extension) or return a safe fallback filename, ensuring callers can handle the
failure.

In `@packages/bruno-electron/src/utils/request-filename.test.js`:
- Around line 1-25: Add unit tests in request-filename.test.js to cover all
branches of getRequestFilename: (1) when item.filename is provided, assert it
returns that filename, (2) when item.type === 'http-request' verify it delegates
to getHttpRequestFilenameBase and respects method/name, and (3) the fallback
branch for graphql/grpc/ws items (e.g., type !== 'http-request') to ensure the
appropriate normalized filename is returned; also add the missing no-collision
base case for getUniqueRequestFilename to assert that when the collision checker
returns false the filename is returned without a counter, and include tests for
normalizeRequestFilename behavior where relevant.
🪄 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

Run ID: 7e4e7300-6838-48ee-b2f2-5b02d0211916

📥 Commits

Reviewing files that changed from the base of the PR and between 415b75d and efdddc2.

📒 Files selected for processing (19)
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js
  • packages/bruno-app/src/components/Sidebar/NewRequest/index.js
  • packages/bruno-app/src/utils/common/requestFilename.js
  • packages/bruno-app/src/utils/common/requestFilename.spec.js
  • packages/bruno-cli/src/utils/collection.js
  • packages/bruno-cli/src/utils/request-filename.js
  • packages/bruno-converters/src/openapi/openapi-common.js
  • packages/bruno-converters/src/openapi/openapi-to-bruno.js
  • packages/bruno-converters/src/openapi/swagger2-to-bruno.js
  • packages/bruno-converters/src/postman/postman-to-bruno.js
  • packages/bruno-converters/src/utils/request-filename.js
  • packages/bruno-converters/tests/openapi/openapi-to-bruno/openapi-to-bruno.spec.js
  • packages/bruno-converters/tests/openapi/swagger2-to-bruno/swagger2-to-bruno.spec.js
  • packages/bruno-converters/tests/postman/postman-to-bruno/postman-to-bruno.spec.js
  • packages/bruno-electron/src/ipc/collection.js
  • packages/bruno-electron/src/ipc/openapi-sync.js
  • packages/bruno-electron/src/utils/collection-import.js
  • packages/bruno-electron/src/utils/request-filename.js
  • packages/bruno-electron/src/utils/request-filename.test.js

Comment on lines 77 to 79
const curlRequestTypeChange = (type) => {
setCurlRequestTypeDetected(type);
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Resync auto filename when cURL type is toggled from the dropdown.

curlRequestTypeChange updates only curlRequestTypeDetected; when filename is still auto-managed, it can stay stale after switching HTTP ↔ GraphQL.

Proposed fix
 const curlRequestTypeChange = (type) => {
   setCurlRequestTypeDetected(type);
+  if (!isFilenameManuallyEdited) {
+    formik.setFieldValue('filename', getRequestFilenameBase({
+      requestName: formik.values.requestName,
+      requestMethod: formik.values.requestMethod,
+      requestType: type
+    }));
+  }
 };

Also applies to: 603-615

🤖 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 `@packages/bruno-app/src/components/Sidebar/NewRequest/index.js` around lines
77 - 79, curlRequestTypeChange currently only calls setCurlRequestTypeDetected,
leaving an auto-generated filename stale when the user toggles between HTTP and
GraphQL; update curlRequestTypeChange to also detect if the filename is in
auto-mode (the auto flag, e.g., isFilenameAuto or filenameAuto) and when true
compute the new auto filename (use the existing helper that derives the auto
name or replicate its logic) and call setFilename with that value (and ensure
the auto flag remains true via setIsFilenameAuto if applicable); apply the same
change pattern to the other handler(s) referenced around lines 603-615 so
toggling the cURL type always resyncs the auto filename.

Comment on lines +20 to +23
const extension = path.extname(filename || '').toLowerCase();
const baseName = ['.bru', '.yml', '.yaml'].includes(extension)
? path.basename(filename, extension)
: filename;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle extension stripping with original-case suffix, not the lowercased variant.

path.basename(filename, extension) may fail when the actual suffix casing differs (e.g. .BRU), so normalization can produce incorrect base names.

Proposed fix
 export const normalizeRequestFilename = (filename, format = 'bru') => {
   const targetExtension = format === 'yml' ? 'yml' : 'bru';
-  const extension = path.extname(filename || '').toLowerCase();
-  const baseName = ['.bru', '.yml', '.yaml'].includes(extension)
-    ? path.basename(filename, extension)
-    : filename;
+  const originalExtension = path.extname(filename || '');
+  const extension = originalExtension.toLowerCase();
+  const baseName = ['.bru', '.yml', '.yaml'].includes(extension)
+    ? path.basename(filename, originalExtension)
+    : filename;
 
   return `${sanitizeName(baseName || 'request')}.${targetExtension}`;
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const extension = path.extname(filename || '').toLowerCase();
const baseName = ['.bru', '.yml', '.yaml'].includes(extension)
? path.basename(filename, extension)
: filename;
const extension = path.extname(filename || '');
const extension = extension.toLowerCase();
const baseName = ['.bru', '.yml', '.yaml'].includes(extension)
? path.basename(filename, extension)
: filename;
Suggested change
const extension = path.extname(filename || '').toLowerCase();
const baseName = ['.bru', '.yml', '.yaml'].includes(extension)
? path.basename(filename, extension)
: filename;
export const normalizeRequestFilename = (filename, format = 'bru') => {
const targetExtension = format === 'yml' ? 'yml' : 'bru';
const originalExtension = path.extname(filename || '');
const extension = originalExtension.toLowerCase();
const baseName = ['.bru', '.yml', '.yaml'].includes(extension)
? path.basename(filename, originalExtension)
: filename;
return `${sanitizeName(baseName || 'request')}.${targetExtension}`;
};
🤖 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 `@packages/bruno-app/src/utils/common/requestFilename.js` around lines 20 - 23,
The code lowercases the extension into the variable extension and then passes
that lowercased value to path.basename, which breaks when the file's actual
suffix has different casing; change the logic to keep a lowercased copy for the
includes check but obtain the original-case suffix (e.g. const origExt =
path.extname(filename)) and pass origExt to path.basename when stripping (use
extensionLower = origExt.toLowerCase() for the includes check and call
path.basename(filename, origExt) when creating baseName), ensuring filename,
extension (origExt), and baseName references are updated accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow requests with the same name (but different HTTP methods)

1 participant