Skip to content

Conversation

@sanish-bruno
Copy link
Collaborator

@sanish-bruno sanish-bruno commented Dec 5, 2025

Follow up to #6217

Problem

There were inconsistencies in how multipart form bodies were handled between normal requests and examples:

  1. Multiline values: Examples were not wrapping multiline values with ''' triple quotes when converting JSON to BRU format
  2. @ContentType annotations: Examples did not support parsing @contentType annotations on multiline values
  3. Value handling: Inconsistent grammar patterns for handling values across different parsers

Solution

This PR fixes the inconsistencies by:

1. Added @ContentType annotation support to example grammars

  • Updated example/bruToJson.js grammar to support @contentType on multiline text blocks
  • Updated example/request/bruToJson.js grammar to support @contentType on multiline text blocks
  • Updated example/response/bruToJson.js grammar to support @contentType on multiline text blocks
  • Added semantic handlers in common/attributes.js to process @contentType annotations

2. Fixed multiline value handling in examples

  • Updated example/jsonToBru.js to use getValueString() which properly wraps multiline values with ''' triple quotes
  • This ensures consistency with how normal requests handle multiline values

3. Standardized value handling across all grammars

  • Updated all example grammars to use the consistent pattern: value = list | multilinetextblock | singlelinevalue
  • Added singlelinevalue handler to common/attributes.js matching the pattern in bruToJson.js
  • Simplified pair handler to work consistently with the new value structure

4. Updated contentType extraction

  • Updated common/semantic-utils.js to use the same regex pattern as bruToJson.js for extracting @contentType from values
  • Both multipartExtractContentType and fileExtractContentType now use consistent patterns

Changes Made

Grammar Updates

  • packages/bruno-lang/v2/src/example/bruToJson.js: Added @contentType support and singlelinevalue pattern
  • packages/bruno-lang/v2/src/example/request/bruToJson.js: Added @contentType support and singlelinevalue pattern
  • packages/bruno-lang/v2/src/example/response/bruToJson.js: Added @contentType support and singlelinevalue pattern

Semantic Handlers

  • packages/bruno-lang/v2/src/common/attributes.js:
    • Added contenttypeannotation handler
    • Updated multilinetextblock handler to match bruToJson.js pattern (splits by newlines, removes 4-char indentation)
    • Added singlelinevalue handler
    • Removed complex value handler (now handled by grammar routing)

Content Type Extraction

  • packages/bruno-lang/v2/src/common/semantic-utils.js:
    • Updated multipartExtractContentType to match pattern in bruToJson.js
    • Updated fileExtractContentType to match pattern in bruToJson.js

JSON to BRU Conversion

  • packages/bruno-lang/v2/src/example/jsonToBru.js:
    • Added getValueString import
    • Updated multipart form conversion to use getValueString() for wrapping multiline values with '''

Testing

Added comprehensive test cases for multiline strings with @contentType annotations:

  • New test fixture: examples-multiline-contenttype.bru with various scenarios:

    • Multiline JSON values with @contentType(application/json)
    • Single-line values with @contentType(text/plain)
    • Multiline values without @contentType
    • Complex nested JSON structures
  • Test cases added:

    • should parse examples with multiline strings and @contentType annotations - Basic parsing verification
    • should correctly extract contentType from multiline values - Detailed contentType extraction verification
    • should handle round-trip conversion for multiline strings with contentType - Round-trip conversion validation

All tests pass ✅

Example

Before this fix, examples with multiline values and @contentType would not parse correctly:

example {
  request: {
    body:multipart-form: {
      test: '''
        {
        "hello" : "there"
        }
      ''' @contentType(application/json)
    }
  }
}

Now this correctly:

  • Parses the multiline value
  • Extracts the @contentType annotation
  • Preserves both through round-trip conversion (BRU → JSON → BRU → JSON)

Breaking Changes

None - this is a bug fix that maintains backward compatibility while fixing inconsistencies.

Related Issues

Fixes inconsistencies in multipart form handling between examples and normal requests.

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

    • Added support for @contentType annotations on multiline text blocks, enabling explicit content type declaration within request/response bodies.
  • Improvements

    • Enhanced handling of multiline values with improved indentation preservation and content type metadata.
    • Optimized Content-Type field editing in multipart form parameters for a more streamlined experience.
  • Tests

    • Added comprehensive test suite validating multiline strings with content type annotations and round-trip conversion integrity.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 5, 2025

Walkthrough

This PR adds support for @contentType annotations after multiline text blocks in the Bruno language parser. Changes include grammar rule extensions across multiple file types, AST attribute handling updates, regex pattern modifications for content type extraction, UI component replacements for single-line content-type editing, and comprehensive test fixtures for validation.

Changes

Cohort / File(s) Summary
Grammar & Parser Rules
packages/bruno-lang/v2/src/example/bruToJson.js, packages/bruno-lang/v2/src/example/request/bruToJson.js, packages/bruno-lang/v2/src/example/response/bruToJson.js
Extended multilinetextblock rules to optionally include @contentType(...) annotations after closing delimiters; introduced new singlelinevalue rule; updated value alternatives to accommodate both single-line and multi-line content.
AST Attribute Handling
packages/bruno-lang/v2/src/common/attributes.js
Modified pair() method to handle array-valued values; replaced value() method with new singlelinevalue() method; updated multilinetextblock() signature to accept contentType parameter and preserve line indentation with conditional contentType appending.
Content Type Extraction
packages/bruno-lang/v2/src/common/semantic-utils.js
Added dotAll flag (s) to regex patterns in multipartExtractContentType and fileExtractContentType to enable newline-inclusive matching.
JSON/BRU Conversion
packages/bruno-lang/v2/src/example/jsonToBru.js
Updated import to include getValueString; wrapped text-type item values using getValueString to support multiline values with triple quotes.
UI Component Updates
packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js, packages/bruno-app/src/components/ResponseExample/ResponseExampleRequestPane/ResponseExampleMultipartFormParams/index.js
Replaced MultiLineEditor with SingleLineEditor for Content-Type field in multipart form params tables.
Test Fixtures & Specifications
packages/bruno-lang/v2/tests/examples/fixtures/bru/examples-multiline-contenttype.bru, packages/bruno-lang/v2/tests/examples/fixtures/json/examples-multiline-contenttype.json, packages/bruno-lang/v2/tests/examples/examples.spec.js
Added new test fixture files (.bru and .json) and comprehensive test suite validating parsing, content type extraction, and round-trip conversion integrity for multiline strings with contentType annotations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Grammar rule consistency: Verify that all three bruToJson files (example, request, response) apply the contentType extension uniformly and that the new singlelinevalue rule is correctly integrated across all variants.
  • AST attribute behavioral changes: Review the modified pair() method's early return for arrays and the new multilinetextblock() signature to ensure indentation stripping and contentType appending logic is correct.
  • Round-trip integrity: Verify that the JSON↔BRU conversion preserves multiline content and contentType metadata through the getValueString usage and semantic-utils regex changes.

Possibly related PRs

  • PR #6217: Directly related—modifies the same Bruno grammar semantics (multilinetextblock, value→singlelinevalue, contentType handling) to add @contentType support for multiline values.

Suggested labels

size/M

Suggested reviewers

  • lohit-bruno
  • helloanoop
  • naman-bruno

Poem

🎨 Content types now dance after multiline strings,
Parsers twirl with @contentType annotations,
Single editors trim the excess sprawl,
Round-trips tested—faithful conversions all.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding @ContentType annotation support and fixing multipart form handling inconsistencies between normal requests and examples.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 934fae1 and 72d6c2a.

📒 Files selected for processing (2)
  • packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js (2 hunks)
  • packages/bruno-app/src/components/ResponseExample/ResponseExampleRequestPane/ResponseExampleMultipartFormParams/index.js (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions. () => {} is good
No space between function name and parentheses. func() not func ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly

Files:

  • packages/bruno-app/src/components/ResponseExample/ResponseExampleRequestPane/ResponseExampleMultipartFormParams/index.js
  • packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: Unit Tests
  • GitHub Check: CLI Tests
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - macOS
  • GitHub Check: SSL Tests - Linux
🔇 Additional comments (4)
packages/bruno-app/src/components/RequestPane/MultipartFormParams/index.js (2)

14-14: SingleLineEditor import is correct and matches usage

The new SingleLineEditor import is clean, follows the existing pattern for editor components, and is only used where expected.


165-182: Content-Type editor swap to SingleLineEditor looks solid

Using SingleLineEditor for the Content-Type cell aligns with the single-line nature of MIME types and keeps the handleParamChange contract intact via the event-shaped onChange wrapper. Props mirror the value editor usage patterns and respect the theming/setup.

packages/bruno-app/src/components/ResponseExample/ResponseExampleRequestPane/ResponseExampleMultipartFormParams/index.js (2)

11-11: SingleLineEditor import is consistent with other editor imports

The added SingleLineEditor import is correct and aligns with how other editor components are brought into this module.


210-226: Content-Type SingleLineEditor wiring matches request pane and editMode semantics

The switch to SingleLineEditor for Content-Type keeps the same event-wrapping pattern, uses readOnly={!editMode} consistently with the rest of the component, and mirrors the Request pane setup. This should give a clearer UX for MIME types while respecting view vs edit modes.

Please confirm that SingleLineEditor fully respects the readOnly prop (no focus, no change events) so we don’t allow accidental edits when editMode is false.


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

Copy link
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: 0

🧹 Nitpick comments (1)
packages/bruno-lang/v2/src/bruToJson.js (1)

422-435: Implementation duplicated with attributes.js.

The multilinetextblock and singlelinevalue handlers are identical to those in attributes.js. Consider extracting these to a shared utility to reduce duplication, though this can be deferred.

// Could extract to a shared module, e.g., in common/semantic-handlers.js:
const createMultilinetextblockHandler = () => (_1, content, _2, _3, contentType) => {
  const multilineString = content.sourceString
    .split('\n')
    .map((line) => line.slice(4))
    .join('\n');
  if (!contentType.sourceString) {
    return multilineString;
  }
  return `${multilineString} ${contentType.sourceString}`;
};
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57222d2 and 934fae1.

📒 Files selected for processing (12)
  • packages/bruno-lang/v2/src/bruToJson.js (5 hunks)
  • packages/bruno-lang/v2/src/common/attributes.js (2 hunks)
  • packages/bruno-lang/v2/src/common/semantic-utils.js (2 hunks)
  • packages/bruno-lang/v2/src/example/bruToJson.js (2 hunks)
  • packages/bruno-lang/v2/src/example/jsonToBru.js (2 hunks)
  • packages/bruno-lang/v2/src/example/request/bruToJson.js (2 hunks)
  • packages/bruno-lang/v2/src/example/response/bruToJson.js (2 hunks)
  • packages/bruno-lang/v2/tests/bruToJson.spec.js (1 hunks)
  • packages/bruno-lang/v2/tests/examples/examples.spec.js (1 hunks)
  • packages/bruno-lang/v2/tests/examples/fixtures/bru/examples-multiline-contenttype.bru (1 hunks)
  • packages/bruno-lang/v2/tests/examples/fixtures/json/examples-multiline-contenttype.json (1 hunks)
  • packages/bruno-tests/collection/echo/echo multipart.bru (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation, never tabs
Use single quotes for strings instead of double quotes
Always add semicolons at the end of statements
No trailing commas in code
Always use parentheses around parameters in arrow functions, even for single parameters
For multiline constructs, put opening braces on the same line with a minimum of 2 elements for multiline formatting
No newlines inside function parentheses
Space before and after the arrow in arrow functions: () => {}
No space between function name and parentheses: func() not func ()
Semicolons should go at the end of the line, not on a new line
Function names should be concise and descriptive
Add JSDoc comments to provide details to abstractions
Avoid single-line abstractions where all that is being done is increasing the call stack with one additional function
Add meaningful comments to explain complex code flow instead of obvious comments

Files:

  • packages/bruno-lang/v2/tests/examples/examples.spec.js
  • packages/bruno-lang/v2/src/common/semantic-utils.js
  • packages/bruno-lang/v2/src/example/response/bruToJson.js
  • packages/bruno-lang/v2/src/example/jsonToBru.js
  • packages/bruno-lang/v2/src/example/request/bruToJson.js
  • packages/bruno-lang/v2/tests/bruToJson.spec.js
  • packages/bruno-lang/v2/src/example/bruToJson.js
  • packages/bruno-lang/v2/src/common/attributes.js
  • packages/bruno-lang/v2/src/bruToJson.js
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

Add tests for any new functionality or meaningful changes; update corresponding tests when code is added, removed, or significantly modified

Files:

  • packages/bruno-lang/v2/tests/examples/examples.spec.js
  • packages/bruno-lang/v2/tests/bruToJson.spec.js
🧠 Learnings (1)
📚 Learning: 2025-12-03T08:09:57.124Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.124Z
Learning: Applies to **/*.{test,spec}.{js,jsx,ts,tsx} : Add tests for any new functionality or meaningful changes; update corresponding tests when code is added, removed, or significantly modified

Applied to files:

  • packages/bruno-lang/v2/tests/examples/examples.spec.js
  • packages/bruno-lang/v2/tests/bruToJson.spec.js
🧬 Code graph analysis (2)
packages/bruno-lang/v2/src/example/jsonToBru.js (1)
packages/bruno-lang/v2/src/utils.js (1)
  • getValueString (35-49)
packages/bruno-lang/v2/tests/bruToJson.spec.js (3)
packages/bruno-lang/v2/tests/dictionary.spec.js (1)
  • expected (10-18)
packages/bruno-lang/v2/src/bruToJson.js (1)
  • parser (1084-1094)
packages/bruno-lang/v2/src/collectionBruToJson.js (1)
  • parser (561-571)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: SSL Tests - macOS
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: Unit Tests
  • GitHub Check: CLI Tests
🔇 Additional comments (20)
packages/bruno-lang/v2/src/example/jsonToBru.js (2)

1-1: LGTM!

The import addition is correct and necessary for the multiline value wrapping functionality.


141-143: LGTM!

The implementation correctly wraps multiline values using getValueString(), which handles empty/null values, preserves single-line values, and wraps multiline values with triple quotes. This fixes the inconsistency between example and normal request handling as described in the PR objectives.

The indentation logic is correct: multiline content receives 2-space indentation from getValueString, then the entire item gets 4 additional spaces from indentStringCustom (line 134), resulting in properly nested output.

packages/bruno-lang/v2/src/common/semantic-utils.js (2)

72-72: LGTM: dotAll flag enables multiline contentType matching.

The s flag addition correctly enables the regex to match @contentType annotations across newlines, which is essential for multiline value support.


88-88: LGTM: Consistent contentType extraction across file types.

Correctly applies the same dotAll flag to file extraction, maintaining consistency with multipart handling.

packages/bruno-tests/collection/echo/echo multipart.bru (2)

15-17: LGTM: Well-structured test for multiline contentType.

The multiline field with @contentType annotation correctly demonstrates the new feature and follows the expected syntax.


27-27: LGTM: Assertion validates end-to-end contentType handling.

The assertion correctly validates that the contentType annotation is properly transmitted in the multipart request.

packages/bruno-lang/v2/src/example/request/bruToJson.js (2)

32-33: LGTM: Grammar extension for contentType annotations.

The grammar additions correctly implement optional @contentType annotations on multiline blocks while maintaining backward compatibility. The st* spacing and optional ? operator are well-placed.


46-47: LGTM: Value rule refactoring improves clarity.

Extracting singlelinevalue as a separate rule enhances grammar readability and aligns with the semantic handler structure.

packages/bruno-lang/v2/tests/bruToJson.spec.js (1)

179-205: LGTM: Comprehensive test for multiline contentType feature.

The test case thoroughly validates parsing of multiline body parts with @contentType annotations, covering the expected structure and ensuring proper newline handling. Well-aligned with coding guidelines for testing new functionality.

packages/bruno-lang/v2/src/example/response/bruToJson.js (1)

26-27: LGTM: Consistent grammar across request/response parsers.

The grammar changes correctly mirror the request parser updates, maintaining consistent multiline contentType handling across both request and response blocks.

Also applies to: 40-41

packages/bruno-lang/v2/src/example/bruToJson.js (1)

28-29: LGTM: Unified grammar implementation.

Grammar changes are consistent with request/response parsers, ensuring uniform contentType handling across the entire example parsing infrastructure.

Also applies to: 42-43

packages/bruno-lang/v2/tests/examples/examples.spec.js (1)

312-376: LGTM: Excellent test coverage for multiline contentType.

The test suite comprehensively validates the new feature through:

  • Parsing verification against fixtures
  • ContentType extraction for multiple field types
  • Round-trip conversion integrity

Tests cover edge cases (with/without contentType) and follow established patterns. Well-aligned with coding guidelines.

packages/bruno-lang/v2/tests/examples/fixtures/bru/examples-multiline-contenttype.bru (1)

1-65: LGTM: Comprehensive test fixture.

The fixture provides excellent coverage of multiline contentType scenarios:

  • Multiline JSON with contentType
  • Single-line values with contentType
  • Multiline content without contentType
  • Complex nested structures

Well-structured and supports thorough testing of the feature.

packages/bruno-lang/v2/tests/examples/fixtures/json/examples-multiline-contenttype.json (1)

1-69: Well-structured test fixture with good coverage.

Covers key scenarios: multiline JSON, plain text, and empty contentType. This will validate the round-trip parsing correctly.

packages/bruno-lang/v2/src/common/attributes.js (3)

54-64: Hardcoded 4-character slice for indentation removal.

The line.slice(4) assumes exactly 4 spaces of indentation. If the indentation varies, this could truncate content or leave extra whitespace. This matches the pattern in bruToJson.js, so it's consistent, but consider adding a brief comment explaining this expectation.


65-67: LGTM!

Clean implementation with proper optional chaining and empty string fallback.


60-63: No issue found; optional grammar elements in Ohm.js always provide a .sourceString property.

Ohm.js returns an empty string ("") when an optional grammar element doesn't match input. The code's conditional check if (!contentType.sourceString) correctly handles this, as empty strings are falsy in JavaScript. Accessing .sourceString on optional elements cannot throw—the property is guaranteed to exist.

packages/bruno-lang/v2/src/bruToJson.js (3)

55-56: Grammar rules for contentType annotation look correct.

The contenttypeannotation rule properly captures the @contentType(...) syntax, and multilinetextblock correctly makes it optional with ?.


69-70: LGTM!

Correct ordering: list and multilinetextblock are prioritized before falling back to singlelinevalue.


214-224: Good use of s (dotAll) flag for multiline content.

The s flag ensures . matches newline characters, which is necessary for multiline values. The non-greedy .*? correctly captures content before the annotation.

@sanish-bruno sanish-bruno force-pushed the fix/example-multipart branch from f160e3d to 6e001ca Compare December 9, 2025 12:47
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.

1 participant