chore: Replace query-string with URLSearchParams (#613)
Co-authored-by: diana-villalvazo-wgu <dianaximena.villalva@wgu.edu>
This commit is contained in:
committed by
GitHub
parent
11a7512fea
commit
a9194261c8
19
package-lock.json
generated
19
package-lock.json
generated
@@ -32,7 +32,6 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"prop-types": "15.8.1",
|
"prop-types": "15.8.1",
|
||||||
"query-string": "7.1.3",
|
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
@@ -16469,24 +16468,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/query-string": {
|
|
||||||
"version": "7.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
|
|
||||||
"integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"decode-uri-component": "^0.2.2",
|
|
||||||
"filter-obj": "^1.1.0",
|
|
||||||
"split-on-first": "^1.0.0",
|
|
||||||
"strict-uri-encode": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/querystringify": {
|
"node_modules/querystringify": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||||
|
|||||||
@@ -52,7 +52,6 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"prop-types": "15.8.1",
|
"prop-types": "15.8.1",
|
||||||
"query-string": "7.1.3",
|
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import queryString from 'query-string';
|
|
||||||
|
|
||||||
import { ListPageSize, SortKeys } from 'data/constants/app';
|
import { ListPageSize, SortKeys } from 'data/constants/app';
|
||||||
import { reduxHooks } from 'hooks';
|
import { reduxHooks } from 'hooks';
|
||||||
import { StrictDict } from 'utils';
|
import { StrictDict } from 'utils';
|
||||||
@@ -27,12 +25,13 @@ export const useCourseListData = () => {
|
|||||||
|
|
||||||
const [sortBy, setSortBy] = module.state.sortBy(SortKeys.enrolled);
|
const [sortBy, setSortBy] = module.state.sortBy(SortKeys.enrolled);
|
||||||
|
|
||||||
const querySearch = queryString.parse(window.location.search, { parseNumbers: true });
|
const querySearch = new URLSearchParams(window.location.search);
|
||||||
|
const disablePagination = querySearch.get('disable_pagination');
|
||||||
|
|
||||||
const { numPages, visibleList } = reduxHooks.useCurrentCourseList({
|
const { numPages, visibleList } = reduxHooks.useCurrentCourseList({
|
||||||
sortBy,
|
sortBy,
|
||||||
filters,
|
filters,
|
||||||
pageSize: querySearch?.disable_pagination === 1 ? 0 : ListPageSize,
|
pageSize: Number(disablePagination) === 1 ? 0 : ListPageSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleRemoveFilter = (filter) => () => removeFilter(filter);
|
const handleRemoveFilter = (filter) => () => removeFilter(filter);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import queryString from 'query-string';
|
|
||||||
|
|
||||||
import { MockUseState } from 'testUtils';
|
import { MockUseState } from 'testUtils';
|
||||||
import { reduxHooks } from 'hooks';
|
import { reduxHooks } from 'hooks';
|
||||||
import { ListPageSize, SortKeys } from 'data/constants/app';
|
import { ListPageSize, SortKeys } from 'data/constants/app';
|
||||||
@@ -15,8 +13,10 @@ jest.mock('hooks', () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('query-string', () => ({
|
const mockGet = jest.fn(() => ({}));
|
||||||
parse: jest.fn(() => ({})),
|
|
||||||
|
global.URLSearchParams = jest.fn().mockImplementation(() => ({
|
||||||
|
get: mockGet,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const state = new MockUseState(hooks);
|
const state = new MockUseState(hooks);
|
||||||
@@ -67,7 +67,7 @@ describe('CourseList hooks', () => {
|
|||||||
it('loads current course list with page size 0 if/when there is query param disable_pagination=1', () => {
|
it('loads current course list with page size 0 if/when there is query param disable_pagination=1', () => {
|
||||||
state.mock();
|
state.mock();
|
||||||
state.mockVal(state.keys.sortBy, testSortBy);
|
state.mockVal(state.keys.sortBy, testSortBy);
|
||||||
queryString.parse.mockReturnValueOnce({ disable_pagination: 1 });
|
mockGet.mockReturnValueOnce('1');
|
||||||
out = hooks.useCourseListData();
|
out = hooks.useCourseListData();
|
||||||
expect(reduxHooks.useCurrentCourseList).toHaveBeenCalledWith({
|
expect(reduxHooks.useCurrentCourseList).toHaveBeenCalledWith({
|
||||||
sortBy: testSortBy,
|
sortBy: testSortBy,
|
||||||
|
|||||||
@@ -1,6 +1,34 @@
|
|||||||
import queryString from 'query-string';
|
|
||||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stringify(query, existingQuery)
|
||||||
|
* simple wrapper to convert an object to a query string
|
||||||
|
* @param {object} query - object to convert
|
||||||
|
* @param {string} existingQuery - existing query string
|
||||||
|
* @returns {string} - query string
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const stringify = (query, existingQuery = '') => {
|
||||||
|
const searchParams = new URLSearchParams(existingQuery);
|
||||||
|
|
||||||
|
Object.entries(query).forEach(([key, value]) => {
|
||||||
|
if (value === undefined || value === null || value === '') {
|
||||||
|
searchParams.delete(key);
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
searchParams.delete(key);
|
||||||
|
value.forEach((val) => {
|
||||||
|
if (val !== undefined && val !== null && val !== '') {
|
||||||
|
searchParams.append(key, val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
searchParams.set(key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return searchParams.toString();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get(url)
|
* get(url)
|
||||||
* simple wrapper providing an authenticated Http client get action
|
* simple wrapper providing an authenticated Http client get action
|
||||||
@@ -10,21 +38,23 @@ export const get = (...args) => getAuthenticatedHttpClient().get(...args);
|
|||||||
/**
|
/**
|
||||||
* post(url, data)
|
* post(url, data)
|
||||||
* simple wrapper providing an authenticated Http client post action
|
* simple wrapper providing an authenticated Http client post action
|
||||||
* queryString.stringify is used to convert the object to query string with = and &
|
* stringify is used to convert the object to query string with = and &
|
||||||
* @param {string} url - target url
|
* @param {string} url - target url
|
||||||
* @param {object|string} body - post payload
|
* @param {object|string} body - post payload
|
||||||
*/
|
*/
|
||||||
export const post = (url, body) => getAuthenticatedHttpClient().post(url, queryString.stringify(body));
|
export const post = (url, body) => getAuthenticatedHttpClient().post(url, stringify(body));
|
||||||
|
|
||||||
export const client = getAuthenticatedHttpClient;
|
export const client = getAuthenticatedHttpClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* stringifyUrl(url, query)
|
* stringifyUrl(url, query)
|
||||||
* simple wrapper around queryString.stringifyUrl that sets skip behavior
|
* simple wrapper to convert a url and query object to a full url
|
||||||
* @param {string} url - base url string
|
* @param {string} url - base url string
|
||||||
* @param {object} query - query parameters
|
* @param {object} query - query parameters
|
||||||
|
* @returns {string} - full url
|
||||||
*/
|
*/
|
||||||
export const stringifyUrl = (url, query) => queryString.stringifyUrl(
|
export const stringifyUrl = (url, query) => {
|
||||||
{ url, query },
|
const [baseUrl, existingQuery = ''] = url.split('?');
|
||||||
{ skipNull: true, skipEmptyString: true },
|
const queryString = stringify(query, existingQuery);
|
||||||
);
|
return queryString ? `${baseUrl}?${queryString}` : baseUrl;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
import queryString from 'query-string';
|
|
||||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||||
import * as utils from './utils';
|
import * as utils from './utils';
|
||||||
|
|
||||||
jest.mock('query-string', () => ({
|
|
||||||
stringifyUrl: jest.fn((url, options) => ({ url, options })),
|
|
||||||
stringify: jest.fn((data) => data),
|
|
||||||
}));
|
|
||||||
jest.mock('@edx/frontend-platform/auth', () => ({
|
jest.mock('@edx/frontend-platform/auth', () => ({
|
||||||
getAuthenticatedHttpClient: jest.fn(),
|
getAuthenticatedHttpClient: jest.fn(),
|
||||||
}));
|
}));
|
||||||
@@ -20,28 +15,25 @@ describe('lms service utils', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('post', () => {
|
describe('post', () => {
|
||||||
it('forwards arguments to authenticatedHttpClient().post', () => {
|
it('forwards arguments to authenticatedHttpClient().post, removes undefined attributes and appends array values', () => {
|
||||||
const post = jest.fn((...args) => ({ post: args }));
|
const post = jest.fn((...args) => ({ post: args }));
|
||||||
getAuthenticatedHttpClient.mockReturnValue({ post });
|
getAuthenticatedHttpClient.mockReturnValue({ post });
|
||||||
const url = 'some url';
|
const url = 'some url';
|
||||||
const body = {
|
const body = {
|
||||||
some: 'body',
|
some: 'body',
|
||||||
for: 'the',
|
for: undefined,
|
||||||
test: 'yay',
|
test: 'yay',
|
||||||
|
array: ['one', 'two', undefined],
|
||||||
};
|
};
|
||||||
const expectedUrl = utils.post(url, body);
|
const expectedUrl = utils.post(url, body);
|
||||||
expect(queryString.stringify).toHaveBeenCalledWith(body);
|
expect(expectedUrl).toEqual(post(url, 'some=body&test=yay&array=one&array=two'));
|
||||||
expect(expectedUrl).toEqual(post(url, body));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('stringifyUrl', () => {
|
describe('stringifyUrl', () => {
|
||||||
it('forwards url and query to stringifyUrl with options to skip null and ""', () => {
|
it('forwards url and query to stringifyUrl skipping null and ""', () => {
|
||||||
const url = 'here.com';
|
const url = 'here.com';
|
||||||
const query = { some: 'set', of: 'queryParams' };
|
const query = { some: 'set', of: 'queryParams' };
|
||||||
const options = { skipNull: true, skipEmptyString: true };
|
expect(utils.stringifyUrl(url, query)).toEqual('here.com?some=set&of=queryParams');
|
||||||
expect(utils.stringifyUrl(url, query)).toEqual(
|
|
||||||
queryString.stringifyUrl({ url, query }, options),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ config.resolve.modules = [
|
|||||||
'node_modules',
|
'node_modules',
|
||||||
];
|
];
|
||||||
|
|
||||||
config.module.rules[0].exclude = /node_modules\/(?!(query-string|split-on-first|strict-uri-encode|@edx))/;
|
config.module.rules[0].exclude = /node_modules\/(?!(split-on-first|strict-uri-encode|@edx))/;
|
||||||
|
|
||||||
config.plugins.push(
|
config.plugins.push(
|
||||||
new CopyPlugin({
|
new CopyPlugin({
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ config.resolve.modules = [
|
|||||||
'node_modules',
|
'node_modules',
|
||||||
];
|
];
|
||||||
|
|
||||||
config.module.rules[0].exclude = /node_modules\/(?!(query-string|split-on-first|strict-uri-encode|@edx))/;
|
config.module.rules[0].exclude = /node_modules\/(?!(split-on-first|strict-uri-encode|@edx))/;
|
||||||
|
|
||||||
config.plugins.push(
|
config.plugins.push(
|
||||||
new CopyPlugin({
|
new CopyPlugin({
|
||||||
|
|||||||
Reference in New Issue
Block a user