Commit 71e9385e authored by Robert Knight's avatar Robert Knight

Omit query params with `undefined` values from API calls

The previous version stated that it aimed to match the behavior of the
`query-string` package but it was actually tested against Node's
`querystring` package, which has slightly different behavior for this
edge case. It omits parameters with `undefined` values and renders empty
parameters for `null` values.

The notebook view was calling `api.search({ uri: undefined, ... })` and
this translated to `/api/search?uri=&...` which caused an error.

Omit parameters instead in this case.
parent 98f66222
...@@ -128,15 +128,18 @@ function createAPICall( ...@@ -128,15 +128,18 @@ function createAPICall(
params params
); );
/** @param {Param?} param */
const serialize = param => param?.toString() ?? '';
const apiURL = new URL(url); const apiURL = new URL(url);
for (let [key, value] of Object.entries(queryParams)) { for (let [key, value] of Object.entries(queryParams)) {
if (Array.isArray(value)) { if (!Array.isArray(value)) {
value.forEach(v => apiURL.searchParams.append(key, serialize(v))); value = [value];
} else { }
apiURL.searchParams.append(key, serialize(value)); for (let item of value) {
// eslint-disable-next-line eqeqeq
if (item == null) {
// Skip all parameters with nullish values.
continue;
}
apiURL.searchParams.append(key, item.toString());
} }
} }
......
...@@ -176,7 +176,7 @@ describe('APIService', () => { ...@@ -176,7 +176,7 @@ describe('APIService', () => {
// Test serialization of nullish parameters in API calls. This behavior matches // Test serialization of nullish parameters in API calls. This behavior matches
// the query-string package that we used to use. // the query-string package that we used to use.
it('sends empty query parameters if value is nullish', () => { it('sends empty query parameters if value is nullish', () => {
expectCall('get', 'search?a=&b=&c=false&d='); expectCall('get', 'search?c=false');
return api.search({ a: undefined, b: null, c: false, d: [null] }); return api.search({ a: undefined, b: null, c: false, d: [null] });
}); });
......
...@@ -7,19 +7,20 @@ ...@@ -7,19 +7,20 @@
* replaceURLParams('/things/:id', {id: 'foo', q: 'bar'}) => * replaceURLParams('/things/:id', {id: 'foo', q: 'bar'}) =>
* {url: '/things/foo', unusedParams: {q: 'bar'}} * {url: '/things/foo', unusedParams: {q: 'bar'}}
* *
* @template Param
* @param {string} url * @param {string} url
* @param {Record<string, string>} params * @param {Record<string, Param>} params
* @return {{ url: string, unusedParams: Record<string, string>}} * @return {{ url: string, unusedParams: Record<string, Param>}}
*/ */
export function replaceURLParams(url, params) { export function replaceURLParams(url, params) {
/** @type {Record<string, string>} */ /** @type {Record<string, Param>} */
const unusedParams = {}; const unusedParams = {};
for (const param in params) { for (const param in params) {
if (params.hasOwnProperty(param)) { if (params.hasOwnProperty(param)) {
const value = params[param]; const value = params[param];
const urlParam = ':' + param; const urlParam = ':' + param;
if (url.indexOf(urlParam) !== -1) { if (url.indexOf(urlParam) !== -1) {
url = url.replace(urlParam, encodeURIComponent(value)); url = url.replace(urlParam, encodeURIComponent(String(value)));
} else { } else {
unusedParams[param] = value; unusedParams[param] = value;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment