diff --git a/packages/bruno-app/src/utils/url/index.js b/packages/bruno-app/src/utils/url/index.js index 335351422..8e10b9391 100644 --- a/packages/bruno-app/src/utils/url/index.js +++ b/packages/bruno-app/src/utils/url/index.js @@ -12,6 +12,32 @@ const hasLength = (str) => { return str.length > 0; }; +const ODATA_PATH_PARAM_REGEX = /(^|[=(,'"`]):([A-Za-z_]\w*)\b/g; + +const decodeODataSegment = (segment) => { + try { + return decodeURIComponent(segment); + } catch (error) { + return segment; + } +}; + +const getODataPathParamNames = (segment) => { + const names = []; + const decodedSegment = decodeODataSegment(segment); + let match; + + while ((match = ODATA_PATH_PARAM_REGEX.exec(decodedSegment))) { + if (match[2]) { + names.push(match[2]); + } + } + + ODATA_PATH_PARAM_REGEX.lastIndex = 0; + + return names; +}; + export const parsePathParams = (url) => { let uri = url.slice(); @@ -49,17 +75,12 @@ export const parsePathParams = (url) => { // 1. EntitySet('key') or EntitySet(key) // 2. EntitySet(Key1=value1,Key2=value2) // 3. Function(param=value) - if (!/^[A-Za-z0-9_.-]+\([^)]*\)$/.test(segment)) { + if (!/^[A-Za-z0-9_.-]+\([^)]*\)$/.test(decodeODataSegment(segment))) { return; } - const paramRegex = /[:](\w+)/g; - let match; - while ((match = paramRegex.exec(segment))) { - if (!match[1]) continue; - - let name = match[1].replace(/[')"`]+$/, ''); - name = name.replace(/^[('"`]+/, ''); + const paramNames = getODataPathParamNames(segment); + for (const name of paramNames) { if (name && !foundParams.has(name)) { foundParams.add(name); } @@ -127,23 +148,18 @@ export const interpolateUrlPathParams = (url, params, variables = {}, options = // 1. EntitySet('key') or EntitySet(key) // 2. EntitySet(Key1=value1,Key2=value2) // 3. Function(param=value) - if (!/^[A-Za-z0-9_.-]+\([^)]*\)$/.test(segment)) { + if (!/^[A-Za-z0-9_.-]+\([^)]*\)$/.test(decodeODataSegment(segment))) { return segment; } - const regex = /[:](\w+)/g; - let match; let result = segment; - while ((match = regex.exec(segment))) { - if (!match[1]) continue; - - let name = match[1].replace(/[')"`]+$/, ''); - name = name.replace(/^[('"`]+/, ''); + const paramNames = getODataPathParamNames(segment); + for (const name of paramNames) { if (!name) continue; const pathParam = params.find((p) => p?.name === name && p?.type === 'path'); if (pathParam) { - result = result.replace(':' + match[1], pathParam.value); + result = result.replace(`:${name}`, pathParam.value); } } return result; diff --git a/packages/bruno-app/src/utils/url/index.spec.js b/packages/bruno-app/src/utils/url/index.spec.js index cd546cc95..0413ffb61 100644 --- a/packages/bruno-app/src/utils/url/index.spec.js +++ b/packages/bruno-app/src/utils/url/index.spec.js @@ -259,6 +259,14 @@ describe('Url Utils - OData parameters', () => { const params = parsePathParams('https://example.com/odata/Products?$filter=Category eq \'{{category}}\'&$orderby={{sortField}}'); expect(params).toEqual([]); }); + + it('should not treat colons inside OData datetime values as path params', () => { + const params = parsePathParams( + 'https://api10.successfactors.com/odata/v2/EmpJob(seqNumber=1L,startDate=datetime\'2021-08-29T00:00:00\',userId=\'213668\')?$format=json' + ); + + expect(params).toEqual([]); + }); }); describe('Url Utils - splitOnFirst', () => { @@ -463,4 +471,28 @@ describe('Url Utils - interpolateUrlPathParams with { raw: true }', () => { expect(result).toEqual('https://example.com/api/:id'); }); + + it('should preserve OData datetime colons when no path params are present', () => { + const url + = 'https://api10.successfactors.com/odata/v2/EmpJob(seqNumber=1L,startDate=datetime\'2021-08-29T00:00:00\',userId=\'213668\')?$format=json'; + + const result = interpolateUrlPathParams(url, [], {}, { raw: true }); + + expect(result).toEqual(url); + }); + + it('should replace only real OData path params and preserve datetime colons', () => { + const url + = 'https://example.com/odata/v2/EmpJob(seqNumber=:seqNumber,startDate=datetime\'2021-08-29T00:00:00\',userId=\':userId\')?$format=json'; + const params = [ + { name: 'seqNumber', type: 'path', enabled: true, value: '1L' }, + { name: 'userId', type: 'path', enabled: true, value: '213668' } + ]; + + const result = interpolateUrlPathParams(url, params, {}, { raw: true }); + + expect(result).toEqual( + 'https://example.com/odata/v2/EmpJob(seqNumber=1L,startDate=datetime\'2021-08-29T00:00:00\',userId=\'213668\')?$format=json' + ); + }); });