Fixes #7148: Handle array values when constructing API URLs

This commit is contained in:
thatmattlove 2021-09-02 08:58:55 -07:00
parent caa2813d0d
commit 113358f2de
13 changed files with 45 additions and 21 deletions

View File

@ -2,6 +2,7 @@
## v3.0.2 (FUTURE)
* [#7131](https://github.com/netbox-community/netbox/issues/7131) - Fix issue where Site fields were hidden when editing a VLAN group
* [#7148](https://github.com/netbox-community/netbox/issues/7148) - Fix issue where static query parameters with multiple values were not queried properly
---

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -174,16 +174,6 @@ export class APISelect {
this.preSorted = true;
}
const emptyOption = base.getAttribute('data-empty-option');
if (isTruthy(emptyOption)) {
this.emptyOption = {
text: emptyOption,
value: '',
};
} else {
this.emptyOption = EMPTY_PLACEHOLDER;
}
if (hasUrl(base)) {
const url = base.getAttribute('data-url') as string;
this.url = url;
@ -197,6 +187,16 @@ export class APISelect {
this.disabledOptions = this.getDisabledOptions();
this.disabledAttributes = this.getDisabledAttributes();
const emptyOption = base.getAttribute('data-empty-option');
if (isTruthy(emptyOption)) {
this.emptyOption = {
text: emptyOption,
value: '',
};
} else {
this.emptyOption = EMPTY_PLACEHOLDER;
}
this.slim = new SlimSelect({
select: this.base,
allowDeselect: true,
@ -295,21 +295,18 @@ export class APISelect {
newOptions = optionsIn.sort((a, b) => (a.text.toLowerCase() > b.text.toLowerCase() ? 1 : -1));
}
// Deduplicate options each time they're set.
let deduplicated = uniqueByProperty(newOptions, 'value');
const deduplicated = uniqueByProperty(newOptions, 'value');
// Determine if the new options have a placeholder.
const hasPlaceholder = typeof deduplicated.find(o => o.value === '') !== 'undefined';
// Get the placeholder index (note: if there is no placeholder, the index will be `-1`).
const placeholderIdx = deduplicated.findIndex(o => o.value === '');
if (hasPlaceholder && placeholderIdx < 0) {
// If there is a placeholder but it is not the first element (due to sorting or other merge
// issues), remove it from the options array and place it in front.
deduplicated.splice(placeholderIdx);
deduplicated = [this.emptyOption, ...deduplicated];
}
if (!hasPlaceholder) {
// If there is no placeholder, add one to the front of the array.
deduplicated = [this.emptyOption, ...deduplicated];
if (hasPlaceholder && placeholderIdx >= 0) {
// If there is an existing placeholder, replace it.
deduplicated[placeholderIdx] = this.emptyOption;
} else {
// If there is not a placeholder, add one to the front.
deduplicated.unshift(this.emptyOption);
}
this._options = deduplicated;
this.slim.setData(deduplicated);

View File

@ -1,6 +1,8 @@
import Cookie from 'cookie';
import queryString from 'query-string';
import type { Stringifiable } from 'query-string';
type Method = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
type ReqData = URLSearchParams | Dict | undefined | unknown;
type SelectedOption = { name: string; options: string[] };
@ -117,6 +119,30 @@ function getCsrfToken(): string {
return value;
}
/**
* Constrict an object from a URL query param string, with all values as an array.
*
* @param params URL search query string.
* @returns Constructed query object.
*/
function queryParamsToObject(params: string): Record<string, Stringifiable[]> {
const result = {} as Record<string, Stringifiable[]>;
const searchParams = new URLSearchParams(params);
for (const [key, originalValue] of searchParams.entries()) {
let value = [] as Stringifiable[];
if (key in result) {
value = [...value, ...result[key]];
}
if (Array.isArray(originalValue)) {
value = [...value, ...originalValue];
} else {
value = [...value, originalValue];
}
result[key] = value;
}
return result;
}
/**
* Build a NetBox URL that includes `settings.BASE_PATH` and enforces leading and trailing slashes.
*
@ -153,7 +179,7 @@ function buildUrl(destination: string): string {
}
const url = combined.join('/');
// Construct an object from the URL search params so it can be re-serialized with the new URL.
const query = Object.fromEntries(new URLSearchParams(search).entries());
const query = queryParamsToObject(search);
return queryString.stringifyUrl({ url, query });
}