Skip to content

fix(datatype): Variable type validation flag reference mismatch issue - #8878

Open
sharan-bruno wants to merge 21 commits into
usebruno:mainfrom
sharan-bruno:fix/variable-type-validation-flag-issue
Open

fix(datatype): Variable type validation flag reference mismatch issue#8878
sharan-bruno wants to merge 21 commits into
usebruno:mainfrom
sharan-bruno:fix/variable-type-validation-flag-issue

Conversation

@sharan-bruno

@sharan-bruno sharan-bruno commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

BRU-3849

Description

Fixes the false data-type warning shown on variable values that reference other variables. DataTypeSelector now resolves the reference before validating.

Problem

variable.value was type-checked literally, so {{port}} typed as number was seen as the string "{{port}}". Result: a bogus "Value is not a valid number" flag even when port is a number.

Fix

Added getReferencedVariableType — extracts the {{...}} name, resolves it via getAllVariables, and returns that variable's real type (supports dotted paths and dotted names). References compare against the referenced type; non-references still go through validateDataTypeValue. Adds a collection prop, already passed by all four call sites.

Screenshots

Before After
image image

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.
  • I've run the claude code review skill locally.

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

    • Variable references are now validated according to their inferred data types.
    • Collection environment references, including nested values, are supported.
    • Existing validation and coercion behavior remains unchanged for literal values.
  • Tests

    • Added coverage for matching and mismatched references across multiple data types.
    • Added collection and environment scenarios to verify consistent type validation.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

DataTypeSelector now resolves collection variables and validates referenced values by inferred type. New fixtures and Playwright coverage test matching and mismatched references in global and collection environments. Environment helpers now use shared tab locators.

Changes

DataTypeSelector validation

Layer / File(s) Summary
Resolve and validate collection variables
packages/bruno-app/src/components/DataTypeSelector/index.js
The component memoizes collection variables, resolves direct and nested {{...}} references, infers their data types, and compares them with the selected type.
Add reference-type fixtures
tests/variable-datatypes/reference-types/fixtures/workspace/*
Fixtures define workspace metadata, typed global and collection variables, and matching or mismatched references, including nested values.
Cover reference type validation
tests/variable-datatypes/reference-types/reference-variable-type.spec.ts, tests/utils/page/actions.ts
The Playwright suite checks literals, matching references, and mismatched references across global and collection environments. Environment helpers select tabs through shared locators.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Playwright
  participant EnvironmentPanel
  participant DataTypeSelector
  participant CollectionVariables
  Playwright->>EnvironmentPanel: open global or collection environment
  EnvironmentPanel->>DataTypeSelector: render variable data type
  DataTypeSelector->>CollectionVariables: resolve {{reference}}
  CollectionVariables-->>DataTypeSelector: return inferred type
  DataTypeSelector-->>Playwright: show or hide mismatch icon
Loading

Possibly related PRs

  • usebruno/bruno#8612: Adds related collection-scoped variable and environment handling for DataTypeSelector.
  • usebruno/bruno#8731: Changes related DataTypeSelector behavior and environment-variable type assertions.

Suggested reviewers: bijin-bruno

Poem

References resolve by type,
Global and collection values align.
Shared tabs guide the test flow,
Nested fields report in line,
Playwright checks each sign.

🚥 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 clearly identifies the fix for variable type validation involving reference mismatches.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 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/DataTypeSelector/index.js`:
- Around line 20-26: Update the variable matching logic around the
value-resolution helper to require the entire trimmed value to consist of a
single {{...}} reference, rather than matching embedded references. Return null
for mixed templates such as prefix {{enabled}}, so getDataTypeFromValue is only
used for complete variable references.
🪄 Autofix

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: 1dce5156-1d5f-49c5-88e5-eac0d05a59e3

📥 Commits

Reviewing files that changed from the base of the PR and between 2d5a2f4 and 1df3911.

📒 Files selected for processing (1)
  • packages/bruno-app/src/components/DataTypeSelector/index.js

Comment thread packages/bruno-app/src/components/DataTypeSelector/index.js Outdated
@pull-request-size pull-request-size Bot added size/L and removed size/S labels Aug 5, 2026

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 4

🤖 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
`@tests/variable-datatypes/reference-types/fixtures/workspace/collections/reference-types/opencollection.yml`:
- Around line 1-3: Add typed collection variables to the reference-types
OpenCollection fixture, then extend reference-variable-type.spec.ts with
assertions covering both matching and mismatched references to those collection
variables. Preserve the existing global-variable cases while ensuring
collection-variable resolution and type validation are exercised.

In
`@tests/variable-datatypes/reference-types/fixtures/workspace/environments/global-environment.yml`:
- Around line 23-69: Add matching and mismatched fixture entries alongside the
existing reference cases for a source variable named globalEnv.boolean, ensuring
references resolve the dotted variable name itself rather than treating it as a
nested object path. Include appropriate typed cases to verify both valid and
invalid type resolution, while preserving the existing globalEnvNestedObject
path cases.

In `@tests/variable-datatypes/reference-types/fixtures/workspace/workspace.yml`:
- Around line 6-8: Update the workspace fixture declaration for the single
collection named reference-types: move its fixture directory from
collections/reference-types to the singular collection/reference-types location
and change the workspace.yml path accordingly.

In `@tests/variable-datatypes/reference-types/reference-variable-type.spec.ts`:
- Around line 1-78: Update the test file’s TypeScript statements to terminate
with semicolons, including the imports, helper function declarations and
returns, locator/assertion calls, and test-step statements throughout the
describe block. Preserve the existing test behavior and structure while applying
the repository’s semicolon style consistently.
🪄 Autofix

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: 3b5e9d1c-9ecd-47ed-9baf-2426014b7981

📥 Commits

Reviewing files that changed from the base of the PR and between 1df3911 and 5aa79e6.

📒 Files selected for processing (7)
  • tests/utils/page/actions.ts
  • tests/variable-datatypes/reference-types/fixtures/workspace/collections/reference-types/environments/collection-environment.yml
  • tests/variable-datatypes/reference-types/fixtures/workspace/collections/reference-types/opencollection.yml
  • tests/variable-datatypes/reference-types/fixtures/workspace/environments/global-environment.yml
  • tests/variable-datatypes/reference-types/fixtures/workspace/workspace.yml
  • tests/variable-datatypes/reference-types/init-user-data/preferences.json
  • tests/variable-datatypes/reference-types/reference-variable-type.spec.ts

Comment on lines +1 to +3
opencollection: "1.0.0"
info:
name: reference-types

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add collection-variable reference fixtures.

This collection defines no variables. The collection-environment cases reference only global variables. Add typed collection variables here and add matching and mismatched reference assertions in reference-variable-type.spec.ts.

Without these cases, the test does not cover collection-variable resolution added by this PR.

🤖 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
`@tests/variable-datatypes/reference-types/fixtures/workspace/collections/reference-types/opencollection.yml`
around lines 1 - 3, Add typed collection variables to the reference-types
OpenCollection fixture, then extend reference-variable-type.spec.ts with
assertions covering both matching and mismatched references to those collection
variables. Preserve the existing global-variable cases while ensuring
collection-variable resolution and type validation are exercised.

Source: Coding guidelines

Comment on lines +6 to +8
collections:
- name: "reference-types"
path: "collections/reference-types"

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the singular fixture directory for one collection.

This workspace declares one collection. Move the fixture to fixtures/workspace/collection/reference-types and change path to collection/reference-types.

Based on learnings: use fixtures/collection when there is one collection.

🤖 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 `@tests/variable-datatypes/reference-types/fixtures/workspace/workspace.yml`
around lines 6 - 8, Update the workspace fixture declaration for the single
collection named reference-types: move its fixture directory from
collections/reference-types to the singular collection/reference-types location
and change the workspace.yml path accordingly.

Source: Learnings

- name: "reference-types"
path: "collections/reference-types"

specs:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

remove spec: , docs: ?

import { Tooltip } from 'react-tooltip';
import { BRUNO_VARIABLE_DATATYPES, parseValueByDataType, validateDataTypeValue } from '@usebruno/common/utils';
import { BRUNO_VARIABLE_DATATYPES, parseValueByDataType, validateDataTypeValue, getDataTypeFromValue } from '@usebruno/common/utils';
import get from 'lodash/get';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: external lib imports always at the top. then @usebruno scoped libraries, then utils from package, then component imports


const DataTypeSelector = ({ variable, onChange, compact = false }) => {
const DataTypeSelector = ({ variable, collection, onChange, compact = false }) => {
const resolvableVariables = useMemo(() => getAllVariables(collection), [collection]);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

getAllVariables re-runs on every mounted row whenever collection, this will cause performance issues, can we hoist this to the parent EnvironmentVariablesTable, add this const resolvableVariables = useMemo(() => getAllVariables(_collection), [_collection]); as a sibling memo to the _collection, and pass it down, so that each row doesn't have to call getAllVariables which is very computationally complex! Also DataTypeSelector shouldn't know about collection at all, Change the prop to resolvableVariables and drop collection

This should be done at all the places we need to have this. so more files needs changes. Be mindful about where all we need to add this.

};

const DataTypeSelector = ({ variable, onChange, compact = false }) => {
const DataTypeSelector = ({ variable, collection, onChange, compact = false }) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also i don't see any files that changed to pass down collection to this component.


const referencedType = getReferencedVariableType(variable.value, resolvableVariables);
const selectedType = variable.dataType || 'string';
const referencedTypeError = referencedType === selectedType ? null : `Value is not a valid ${selectedType}`;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Better to give the responsibility to validateDataTypeValue that having referencedTypeError here , keep the concerns together may be something like this

export const validateTypedVariableValue = (value, dataType, resolvableVariables) => {
    const referencedType = getReferencedVariableType(value, resolvableVariables);
    if (referencedType !== null) {
      return referencedType === dataType ? null : `Value is not a valid ${dataType}`;
    }
    return validateDataTypeValue(parseValueByDataType(value, dataType), dataType);
  };

const handleTypeChange = (type) => {
onChange({ dataType: type === 'string' ? undefined : type });
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Taking a tangent from existing implementation , considering unit testability and responsibility, why can't we think of something like this

In packages/bruno-common/src/utils/datatype.ts

  // Resolves `path` on `obj`, matching lodash.get semantics we rely on:
  // a literal key that contains dots wins over walking the same-looking dotted path.
  // (Env vars can be named e.g. "globalEnvObject.port" as a flat key.)
  const getByPath = (obj: Record<string, any>, path: string): any => {
    if (obj == null) return undefined;
    if (Object.prototype.hasOwnProperty.call(obj, path)) return obj[path];
    return path.split('.').reduce<any>((acc, key) => (acc == null ? undefined : acc[key]), obj);
  };

  // Whole-string template reference only: '{{a.b}}' resolves, 'x-{{a}}-y' does not.
  const resolveWholeReference = (value: any, resolvableVariables: Record<string, any>): any => {
    if (typeof value !== 'string') return undefined;
    const match = value.trim().match(/^\{\{([^}]+)\}\}$/);
    if (!match) return undefined;
    return getByPath(resolvableVariables, match[1].trim());
  };

  // string-form → typed JS value, or raw on failure. When `resolvableVariables`
  // is provided and `value` is a whole-string template reference, the resolved
  // JS value is returned as-is (its declared type is authoritative — we don't
  // re-coerce it).
  export const parseValueByDataType = (
    value: any,
    dataType?: BrunoVariableDataType,
    resolvableVariables?: Record<string, any>
  ): any => {
    if (resolvableVariables) {
      const resolved = resolveWholeReference(value, resolvableVariables);
      if (resolved !== undefined) return resolved;
    }
    if (!dataType || dataType === 'string') return value;
    // ... existing number / boolean / object branches unchanged ...
  };

in packages/bruno-app/src/components/DataTypeSelector/index.js

  const DataTypeSelector = ({ variable, resolvableVariables, onChange, compact = false }) => {
    const selectedType = variable.dataType || 'string';
    const coercedValue = parseValueByDataType(variable.value, selectedType, resolvableVariables);
    const typeError = validateDataTypeValue(coercedValue, selectedType);
    // ... rest unchanged
  };

and unit test cases in packages/bruno-common/src/utils/datatype.spec.ts

// Returns an error message when post-coerce value's JS type doesn't match dataType.
export const validateDataTypeValue = (value: any, dataType?: BrunoVariableDataType): string | null => {
if (!dataType || dataType === 'string') return null;
if (!dataType) return null;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We were returning null previously when dataType === 'string' , please check the consumers of this function, and ensure that we are not introducing any regressions

dataType?: BrunoVariableDataType,
resolvableVariables?: Record<string, any>
): any => {
if (resolvableVariables) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

we will need to pass down the resolved value to check if they are parsable as a number, boolean, object, than return the value as we were doing before, we should try to parse/coerce the value to a a certain type then return it. Thoughts on this!

describe('parseValueByDataType — {{var}} references', () => {
const variables = { count: 7, flag: true, payload: { a: 1 }, label: 'hi', nested: { count: 3 } };

it('resolves a lone reference to the referenced variable value', () => {

@sanish-bruno sanish-bruno Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

coercion test cases should also be part of the test suite, especially variableReferences, where

obj : {
 "count": 42
}

{{obj.count}} should get parsed as string as well as number

Please also add the basic coercion test cases if they are missing

it('returns empty string for functions and symbols', () => {
expect(valueToString(() => 42)).toBe('');
expect(valueToString(function named() {})).toBe('');
expect(valueToString(function named() { })).toBe('');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@sharan-bruno this is intentional right, Linting?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

formatting issue, i will remove it

"defaultWorkspacePath": "{{workspacePath}}"
}
}
} No newline at end of file

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

add a newline


// string-form → typed JS value, or raw on failure.
export const parseValueByDataType = (value: any, dataType?: BrunoVariableDataType): any => {
export const parseValueByDataType = (

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

may need to update the title as there is an additional change including resolvableVariables

});

await test.step('Valid literal values are not flagged', async () => {
await expectNotFlagged(page, 'globalEnvString');

@sanish-bruno sanish-bruno Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@sharan-bruno In the current state of the test cases, it is very difficult to track which paths are tested, which are not, i think we may need to tweak the test cases do that it is easier to review, maintain. One should be able to understand, what is being tested, what is missed quickly and should be able to decisively add things so that the testing is robust.

Now there is a lot of mental overhead to read each of the variable names, and keeping all those context in mind and work with these files will be difficult to pull through.

Similar approach can also be adopted for unit test cases

Please check #8943 , Please use discretion, while making these changes

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@sanish-bruno Agreed. I’ve refactored the test cases using a matrix-based approach, making the coverage and expected behavior easier to understand and maintain

Comment thread packages/bruno-common/src/utils/datatype.references.spec.ts Outdated
Comment thread packages/bruno-common/src/utils/datatype.references.spec.ts Outdated
Comment thread packages/bruno-common/src/utils/datatype.references.spec.ts Outdated
sanish-bruno
sanish-bruno previously approved these changes Aug 14, 2026
Comment thread tests/utils/page/actions.ts
const mismatchIcon = async (page: Page, name: string) => {
const locators = buildCommonLocators(page);
const row = locators.environment.varRow(name);
await scrollVirtuosoRowIntoView(page, row);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This flaked on CI. The row was missing because the table had not rendered yet. Can we wait for it first?

@sharan-bruno sharan-bruno Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

added wait before checking this

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is still flaky

@sharan-bruno
sharan-bruno force-pushed the fix/variable-type-validation-flag-issue branch from d787f77 to 19aaec4 Compare August 18, 2026 07:55
const onSave = () => dispatch(saveRequest(item.uid, collection.uid));
const handleRun = () => dispatch(sendRequest(item, collection.uid));

const resolvableVariables = useMemo(() => getAllVariables(collection), [collection]);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should this pass item too?

getAllVariables(collection, item)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

added


const onSave = () => dispatch(saveFolderRoot(collection.uid, folder.uid));

const resolvableVariables = useMemo(() => getAllVariables(collection), [collection]);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should this pass folder too?

getAllVariables(collection, folder)

cc: @sanish-bruno

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

added folder

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants