From 40298b96a468a1c7d2e9058c5b73765fd83afe8b Mon Sep 17 00:00:00 2001 From: Pooja Date: Wed, 1 Apr 2026 20:08:31 +0530 Subject: [PATCH] fix: preserve query params without values by not appending = sign (#7567) * fix: preserve query params without values by not appending = sign * fix: parseCurlCommand test --- .../src/utils/curl/parse-curl.spec.js | 2 +- .../bruno-common/src/utils/url/index.spec.ts | 43 ++++++++++++++++++- packages/bruno-common/src/utils/url/index.ts | 12 +++++- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/packages/bruno-app/src/utils/curl/parse-curl.spec.js b/packages/bruno-app/src/utils/curl/parse-curl.spec.js index 7f24aba78..88629fae1 100644 --- a/packages/bruno-app/src/utils/curl/parse-curl.spec.js +++ b/packages/bruno-app/src/utils/curl/parse-curl.spec.js @@ -902,7 +902,7 @@ describe('parseCurlCommand', () => { { name: 'test', value: 'urlquery' }, { name: 'name', value: 'John%20Doe' }, { name: 'email', value: 'john@example.com' }, - { name: 'hello', value: '' } + { name: 'hello', value: undefined } ] }); }); diff --git a/packages/bruno-common/src/utils/url/index.spec.ts b/packages/bruno-common/src/utils/url/index.spec.ts index c36c4d75d..178297b83 100644 --- a/packages/bruno-common/src/utils/url/index.spec.ts +++ b/packages/bruno-common/src/utils/url/index.spec.ts @@ -50,6 +50,18 @@ describe('encodeUrl', () => { expect(encodeUrl(url)).toBe(expected); }); + it('should handle query parameters without values (no = sign)', () => { + const url = 'https://example.com/api?flag&age=25&verbose'; + const expected = 'https://example.com/api?flag&age=25&verbose'; + expect(encodeUrl(url)).toBe(expected); + }); + + it('should handle mixed empty-value and no-value parameters', () => { + const url = 'https://example.com/api?seat=&table=2&flag'; + const expected = 'https://example.com/api?seat=&table=2&flag'; + expect(encodeUrl(url)).toBe(expected); + }); + it('should encode query parameters with pipe operator', () => { const url = 'https://example.com/api?filter=status|active&sort=name|asc&tags=frontend|backend|api'; const expected = 'https://example.com/api?filter=status%7Cactive&sort=name%7Casc&tags=frontend%7Cbackend%7Capi'; @@ -159,7 +171,7 @@ describe('parseQueryParams', () => { expect(result).toEqual([]); }); - it('should handle query parameters with empty values', () => { + it('should handle query parameters with empty values (has = sign)', () => { const queryString = 'name=&age=25&active='; const result = parseQueryParams(queryString); expect(result).toEqual([ @@ -169,6 +181,16 @@ describe('parseQueryParams', () => { ]); }); + it('should handle query parameters without values (no = sign)', () => { + const queryString = 'flag&age=25&verbose'; + const result = parseQueryParams(queryString); + expect(result).toEqual([ + { name: 'flag', value: undefined }, + { name: 'age', value: '25' }, + { name: 'verbose', value: undefined } + ]); + }); + it('should extract query parameters with pipe operator', () => { const queryString = 'filter=status|active&sort=name|asc&tags=frontend|backend'; const result = parseQueryParams(queryString); @@ -218,4 +240,23 @@ describe('buildQueryString', () => { const result = buildQueryString(params, { encode: false }); expect(result).toBe('filter=status|active&sort=name|asc'); }); + + it('should omit = for params with undefined value', () => { + const params = [ + { name: 'flag', value: undefined }, + { name: 'age', value: '25' }, + { name: 'verbose' } + ]; + const result = buildQueryString(params); + expect(result).toBe('flag&age=25&verbose'); + }); + + it('should include = for params with empty string value', () => { + const params = [ + { name: 'seat', value: '' }, + { name: 'table', value: '2' } + ]; + const result = buildQueryString(params); + expect(result).toBe('seat=&table=2'); + }); }); diff --git a/packages/bruno-common/src/utils/url/index.ts b/packages/bruno-common/src/utils/url/index.ts index 58dabb018..466dca59f 100644 --- a/packages/bruno-common/src/utils/url/index.ts +++ b/packages/bruno-common/src/utils/url/index.ts @@ -16,8 +16,12 @@ function buildQueryString(paramsArray: QueryParam[], { encode = false }: BuildQu .filter(({ name }) => typeof name === 'string' && name.trim().length > 0) .map(({ name, value }) => { const finalName = encode ? encodeURIComponent(name) : name; - const finalValue = encode ? encodeURIComponent(value ?? '') : (value ?? ''); + if (value === undefined) { + return finalName; + } + + const finalValue = encode ? encodeURIComponent(value) : value; return `${finalName}=${finalValue}`; }) .join('&'); @@ -39,9 +43,13 @@ function parseQueryParams(query: string, { decode = false }: ExtractQueryParamsO return null; } + // Distinguish between ?param (no '=' at all) and ?param= (has '=' with empty value) + const hasEqualsSign = pair.includes('='); + const value = hasEqualsSign ? (decode ? decodeURIComponent(valueParts.join('=')) : valueParts.join('=')) : undefined; + return { name: decode ? decodeURIComponent(name) : name, - value: decode ? decodeURIComponent(valueParts.join('=')) : valueParts.join('=') + value }; }).filter((param): param is NonNullable => param !== null);