Skip to content

Commit eb4e780

Browse files
committed
refactor: add shared parseQueryParamList utility for query parameter parsing
- Added parseQueryParamList() function to handle parsing of query parameters that can be either strings or arrays, with support for comma-separated values - Exported both parseCommaSeparatedList and parseQueryParamList from index-internals.ts for use in internal packages - Added 11 comprehensive test cases covering string/array parsing, comma-separation with/without spaces, empty values, and whitespace handling - This eliminates duplicate parsing logic and provides a single source of truth for query parameter handling
1 parent 6f60e27 commit eb4e780

File tree

3 files changed

+82
-1
lines changed

3 files changed

+82
-1
lines changed

src/index-internals.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { defaultTools, getActorsAsTools, toolCategories,
1010
toolCategoriesEnabledByDefault, unauthEnabledToolCategories, unauthEnabledTools } from './tools/index.js';
1111
import { actorNameToToolName } from './tools/utils.js';
1212
import type { ToolCategory } from './types.js';
13+
import { parseCommaSeparatedList, parseQueryParamList } from './utils/generic.js';
1314
import { getExpectedToolNamesByCategories, getToolPublicFieldOnly } from './utils/tools.js';
1415
import { TTLLRUCache } from './utils/ttl-lru.js';
1516

@@ -30,4 +31,6 @@ export {
3031
getToolPublicFieldOnly,
3132
unauthEnabledToolCategories,
3233
unauthEnabledTools,
34+
parseCommaSeparatedList,
35+
parseQueryParamList,
3336
};

src/utils/generic.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,27 @@ export function parseCommaSeparatedList(input?: string): string[] {
1515
return input.split(',').map((s) => s.trim()).filter((s) => s.length > 0);
1616
}
1717

18+
/**
19+
* Parses a query parameter that can be either a string or an array of strings.
20+
* Handles comma-separated values in strings and filters out empty values.
21+
*
22+
* @param param - A query parameter that can be a string, array of strings, or undefined
23+
* @returns An array of trimmed, non-empty strings
24+
* @example
25+
* parseQueryParamList("a,b,c"); // ["a", "b", "c"]
26+
* parseQueryParamList(["a", "b"]); // ["a", "b"]
27+
* parseQueryParamList(undefined); // []
28+
*/
29+
export function parseQueryParamList(param?: string | string[]): string[] {
30+
if (!param) {
31+
return [];
32+
}
33+
if (Array.isArray(param)) {
34+
return param.flatMap((item) => parseCommaSeparatedList(item));
35+
}
36+
return parseCommaSeparatedList(param);
37+
}
38+
1839
/**
1940
* Recursively gets the value in a nested object for each key in the keys array.
2041
* Each key can be a dot-separated path (e.g. 'a.b.c').

tests/unit/utils.generic.test.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, expect, it } from 'vitest';
22

3-
import { getValuesByDotKeys, isValidHttpUrl, parseBooleanFromString, parseCommaSeparatedList } from '../../src/utils/generic.js';
3+
import { getValuesByDotKeys, isValidHttpUrl, parseBooleanFromString, parseCommaSeparatedList, parseQueryParamList } from '../../src/utils/generic.js';
44

55
describe('getValuesByDotKeys', () => {
66
it('should get value for a key without dot', () => {
@@ -83,6 +83,63 @@ describe('parseCommaSeparatedList', () => {
8383
});
8484
});
8585

86+
describe('parseQueryParamList', () => {
87+
it('should parse comma-separated string', () => {
88+
const result = parseQueryParamList('tool1, tool2, tool3');
89+
expect(result).toEqual(['tool1', 'tool2', 'tool3']);
90+
});
91+
92+
it('should parse comma-separated string without spaces', () => {
93+
const result = parseQueryParamList('tool1,tool2,tool3');
94+
expect(result).toEqual(['tool1', 'tool2', 'tool3']);
95+
});
96+
97+
it('should parse array of strings', () => {
98+
const result = parseQueryParamList(['tool1', 'tool2', 'tool3']);
99+
expect(result).toEqual(['tool1', 'tool2', 'tool3']);
100+
});
101+
102+
it('should handle undefined input', () => {
103+
const result = parseQueryParamList(undefined);
104+
expect(result).toEqual([]);
105+
});
106+
107+
it('should handle empty string', () => {
108+
const result = parseQueryParamList('');
109+
expect(result).toEqual([]);
110+
});
111+
112+
it('should handle empty array', () => {
113+
const result = parseQueryParamList([]);
114+
expect(result).toEqual([]);
115+
});
116+
117+
it('should flatten array with comma-separated values', () => {
118+
const result = parseQueryParamList(['tool1, tool2', 'tool3, tool4']);
119+
expect(result).toEqual(['tool1', 'tool2', 'tool3', 'tool4']);
120+
});
121+
122+
it('should filter empty strings from array', () => {
123+
const result = parseQueryParamList(['tool1', '', 'tool2']);
124+
expect(result).toEqual(['tool1', 'tool2']);
125+
});
126+
127+
it('should handle single tool in string', () => {
128+
const result = parseQueryParamList('single-tool');
129+
expect(result).toEqual(['single-tool']);
130+
});
131+
132+
it('should handle single tool in array', () => {
133+
const result = parseQueryParamList(['single-tool']);
134+
expect(result).toEqual(['single-tool']);
135+
});
136+
137+
it('should trim whitespace from array items and their comma-separated values', () => {
138+
const result = parseQueryParamList([' tool1 , tool2 ', ' tool3']);
139+
expect(result).toEqual(['tool1', 'tool2', 'tool3']);
140+
});
141+
});
142+
86143
describe('isValidUrl', () => {
87144
it('should validate correct URLs', () => {
88145
expect(isValidHttpUrl('http://example.com')).toBe(true);

0 commit comments

Comments
 (0)