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 content type annotations on multiline text blocks. Users can now specify content types like @contentType(application/json) or @contentType(text/plain) for multiline content in multipart forms, requests, and response bodies. Comprehensive test coverage ensures reliable round-trip conversion between formats.

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 5, 2025

Walkthrough

Extends the Bruno language parser to support content type annotations on multiline text blocks using @contentType(...) syntax. Updates grammar rules across multiple parser modules, implements semantic handlers for content type preservation, and adds comprehensive test coverage with fixtures.

Changes

Cohort / File(s) Summary
Grammar rules for content type support
packages/bruno-lang/v2/src/bruToJson.js, 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
Introduces contenttypeannotation rule and singlelinevalue rule; extends multilinetextblock to accept optional content type annotations after closing delimiter. Replaces direct valuechar* handling with new singlelinevalue pattern.
Semantic implementation
packages/bruno-lang/v2/src/common/attributes.js, packages/bruno-lang/v2/src/common/semantic-utils.js
Adds singlelinevalue(chars) semantic; updates multilinetextblock signature to accept and append contentType parameter. Removes legacy value(chars) method. Adds dotAll flag (s) to regex patterns in content type extraction to match across newlines.
JSON-to-BRU conversion
packages/bruno-lang/v2/src/example/jsonToBru.js
Wraps text-type item values using getValueString to support multiline values with triple quotes.
Test specifications
packages/bruno-lang/v2/tests/bruToJson.spec.js, packages/bruno-lang/v2/tests/examples/examples.spec.js
Adds test cases for multiline body parts with content type annotation; validates parsing, extraction, and round-trip (BRU → JSON → BRU) integrity for multiline strings with @contentType.
Test fixtures
packages/bruno-lang/v2/tests/examples/fixtures/bru/examples-multiline-contenttype.bru, packages/bruno-lang/v2/tests/examples/fixtures/json/examples-multiline-contenttype.json
Introduces fixture files with multipart-form examples containing multiline fields annotated with various content types (application/json, text/plain).
Integration test update
packages/bruno-tests/collection/echo/echo\ multipart.bru
Adds multiline field with @contentType(application/json--multiline--test) annotation and updates response assertion to verify content type header.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Multiple grammar rule additions across four parser modules follow a consistent pattern
  • Semantic implementation changes are moderate in complexity; requires verification of content type preservation through the parsing pipeline
  • Regex flag changes in semantic-utils require careful review to ensure dotAll behavior doesn't introduce unintended matches
  • Test coverage is comprehensive but fixtures require validation against actual expected behavior
  • Key areas for focus:
    • Verify multilinetextblock semantic correctly appends content type only when present
    • Confirm singlelinevalue replacement doesn't break existing single-line value parsing
    • Validate regex dotAll flag changes don't over-match content across intended boundaries
    • Review round-trip test to ensure full fidelity in BRU ↔ JSON conversions

Poem

Multiline strings now wear their types,
With @contentType decorative stripes,
Round-tripping through JSON's embrace,
Content types hold their rightful place. ✨

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 describes the main change: adding @ContentType support to multipart form handling in examples and fixing related inconsistencies.
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

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

Copy link

@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.

Márk Dániel Seres and others added 3 commits December 6, 2025 01:22
Fixes the issue where the @ContentType annotation broke the parsing of multiline values.
Not strictly needed since body:file uses single-line values in practice,
but doesn't hurt and matches what multipartExtractContentType does.
@sanish-bruno sanish-bruno force-pushed the fix/example-multipart branch from 934fae1 to f160e3d Compare December 5, 2025 19:52
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