From 8b2844ba42ca7fd5455ead2a8b2027c11d6a5fbb Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 31 Jul 2018 09:33:26 +0100 Subject: [PATCH] Update the URL package to be PHP compliant --- core-blocks/embed/index.js | 6 ++++-- core-blocks/latest-posts/edit.js | 14 +++++++------- package-lock.json | 14 ++++---------- packages/components/package.json | 2 +- .../components/src/server-side-render/index.js | 8 +++++--- packages/editor/package.json | 1 - .../src/components/page-attributes/parent.js | 8 ++++---- .../post-taxonomies/flat-term-selector.js | 8 +++++--- .../post-taxonomies/hierarchical-term-selector.js | 11 ++++++++--- packages/editor/src/components/url-input/index.js | 6 +++--- packages/url/package.json | 3 ++- packages/url/src/index.js | 10 +++++----- packages/url/src/test/index.test.js | 7 +++++++ 13 files changed, 55 insertions(+), 43 deletions(-) diff --git a/core-blocks/embed/index.js b/core-blocks/embed/index.js index 0d98464636903..e30a87125121a 100644 --- a/core-blocks/embed/index.js +++ b/core-blocks/embed/index.js @@ -3,7 +3,6 @@ */ import { parse } from 'url'; import { includes, kebabCase, toLower } from 'lodash'; -import httpBuildQuery from 'http-build-query'; import memoize from 'memize'; import classnames from 'classnames'; @@ -16,6 +15,7 @@ import { Button, Placeholder, Spinner, SandBox, IconButton, Toolbar } from '@wor import { createBlock } from '@wordpress/blocks'; import { RichText, BlockControls } from '@wordpress/editor'; import apiFetch from '@wordpress/api-fetch'; +import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies @@ -27,7 +27,9 @@ import './editor.scss'; const HOSTS_NO_PREVIEWS = [ 'facebook.com' ]; // Caches the embed API calls, so if blocks get transformed, or deleted and added again, we don't spam the API. -const wpEmbedAPI = memoize( ( url ) => apiFetch( { path: `/oembed/1.0/proxy?${ httpBuildQuery( { url } ) }` } ) ); +const wpEmbedAPI = memoize( ( url ) => + apiFetch( { path: addQueryArgs( '/oembed/1.0/proxy', { url } ) } ) +); const matchesPatterns = ( url, patterns = [] ) => { return patterns.some( ( pattern ) => { diff --git a/core-blocks/latest-posts/edit.js b/core-blocks/latest-posts/edit.js index 2570bf49175dd..f269ce2014c1d 100644 --- a/core-blocks/latest-posts/edit.js +++ b/core-blocks/latest-posts/edit.js @@ -4,7 +4,6 @@ import { get, isUndefined, pickBy } from 'lodash'; import moment from 'moment'; import classnames from 'classnames'; -import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -27,6 +26,7 @@ import { BlockAlignmentToolbar, BlockControls, } from '@wordpress/editor'; +import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies @@ -160,19 +160,19 @@ class LatestPostsEdit extends Component { export default withAPIData( ( props ) => { const { postsToShow, order, orderBy, categories } = props.attributes; - const latestPostsQuery = httpBuildQuery( pickBy( { + const latestPostsQuery = pickBy( { categories, order, orderby: orderBy, per_page: postsToShow, _fields: [ 'date_gmt', 'link', 'title' ], - }, ( value ) => ! isUndefined( value ) ) ); - const categoriesListQuery = httpBuildQuery( { + }, ( value ) => ! isUndefined( value ) ); + const categoriesListQuery = { per_page: 100, _fields: [ 'id', 'name', 'parent' ], - } ); + }; return { - latestPosts: `/wp/v2/posts?${ latestPostsQuery }`, - categoriesList: `/wp/v2/categories?${ categoriesListQuery }`, + latestPosts: addQueryArgs( '/wp/v2/posts', latestPostsQuery ), + categoriesList: addQueryArgs( '/wp/v2/categories', categoriesListQuery ), }; } )( LatestPostsEdit ); diff --git a/package-lock.json b/package-lock.json index 6a52378001749..380aa4bf272ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3086,11 +3086,11 @@ "@wordpress/i18n": "file:packages/i18n", "@wordpress/is-shallow-equal": "file:packages/is-shallow-equal", "@wordpress/keycodes": "file:packages/keycodes", + "@wordpress/url": "file:packages/url", "classnames": "^2.2.5", "clipboard": "^1.7.1", "dom-scroll-into-view": "^1.2.1", "element-closest": "^2.0.2", - "http-build-query": "^0.7.0", "lodash": "^4.17.10", "memize": "^1.0.5", "moment": "^2.22.1", @@ -3469,7 +3469,6 @@ "dom-react": "^2.2.1", "dom-scroll-into-view": "^1.2.1", "element-closest": "^2.0.2", - "http-build-query": "^0.7.0", "lodash": "^4.17.10", "memize": "^1.0.5", "react-autosize-textarea": "^3.0.2", @@ -3715,7 +3714,8 @@ "@wordpress/url": { "version": "file:packages/url", "requires": { - "@babel/runtime": "^7.0.0-beta.52" + "@babel/runtime": "^7.0.0-beta.52", + "qs": "^6.5.2s" } }, "@wordpress/viewport": { @@ -10548,11 +10548,6 @@ "readable-stream": "^2.0.2" } }, - "http-build-query": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/http-build-query/-/http-build-query-0.7.0.tgz", - "integrity": "sha512-r5jnQ/PcKzFg2dLJAj9xU8cBHuLubxLli6NiFtziNrDqVwPcjIgioamZszzGOPJq6ekUu+WfIcgsuPqe9MX4Ag==" - }, "http-cache-semantics": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", @@ -16890,8 +16885,7 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "query-string": { "version": "5.1.1", diff --git a/packages/components/package.json b/packages/components/package.json index d66d8b7abae37..35287e33190c0 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -30,11 +30,11 @@ "@wordpress/i18n": "file:../i18n", "@wordpress/is-shallow-equal": "file:../is-shallow-equal", "@wordpress/keycodes": "file:../keycodes", + "@wordpress/url": "file:../url", "classnames": "^2.2.5", "clipboard": "^1.7.1", "dom-scroll-into-view": "^1.2.1", "element-closest": "^2.0.2", - "http-build-query": "^0.7.0", "lodash": "^4.17.10", "memize": "^1.0.5", "moment": "^2.22.1", diff --git a/packages/components/src/server-side-render/index.js b/packages/components/src/server-side-render/index.js index e6031903e6509..29814b08ed2b3 100644 --- a/packages/components/src/server-side-render/index.js +++ b/packages/components/src/server-side-render/index.js @@ -12,7 +12,7 @@ import { } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; import apiFetch from '@wordpress/api-fetch'; -import httpBuildQuery from 'http-build-query'; +import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies. @@ -21,8 +21,10 @@ import Placeholder from '../placeholder'; import Spinner from '../spinner'; export function rendererPathWithAttributes( block, attributes = null ) { - return `/gutenberg/v1/block-renderer/${ block }?context=edit` + - ( null !== attributes ? '&' + httpBuildQuery( { attributes } ) : '' ); + return addQueryArgs( `/gutenberg/v1/block-renderer/${ block }`, { + context: 'edit', + ...( null !== attributes ? { attributes } : {} ), + } ); } export class ServerSideRender extends Component { diff --git a/packages/editor/package.json b/packages/editor/package.json index 7ca7fb6061f7f..54de007a90091 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -46,7 +46,6 @@ "dom-react": "^2.2.1", "dom-scroll-into-view": "^1.2.1", "element-closest": "^2.0.2", - "http-build-query": "^0.7.0", "lodash": "^4.17.10", "memize": "^1.0.5", "react-autosize-textarea": "^3.0.2", diff --git a/packages/editor/src/components/page-attributes/parent.js b/packages/editor/src/components/page-attributes/parent.js index 53077c642aca3..e07ed64e2c79a 100644 --- a/packages/editor/src/components/page-attributes/parent.js +++ b/packages/editor/src/components/page-attributes/parent.js @@ -2,7 +2,6 @@ * External dependencies */ import { get } from 'lodash'; -import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -11,6 +10,7 @@ import { __ } from '@wordpress/i18n'; import { TreeSelect, withAPIData } from '@wordpress/components'; import { compose } from '@wordpress/compose'; import { withSelect, withDispatch } from '@wordpress/data'; +import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies @@ -65,7 +65,7 @@ const applyWithDispatch = withDispatch( ( dispatch ) => { const applyWithAPIDataItems = withAPIData( ( { postType, postId } ) => { const isHierarchical = get( postType, [ 'hierarchical' ], false ); const restBase = get( postType, [ 'rest_base' ], false ); - const queryString = httpBuildQuery( { + const queryString = { context: 'edit', per_page: -1, exclude: postId, @@ -73,9 +73,9 @@ const applyWithAPIDataItems = withAPIData( ( { postType, postId } ) => { _fields: [ 'id', 'parent', 'title' ], orderby: 'menu_order', order: 'asc', - } ); + }; return isHierarchical && restBase ? - { items: `/wp/v2/${ restBase }?${ queryString }` } : + { items: addQueryArgs( `/wp/v2/${ restBase }`, queryString ) } : {}; } ); diff --git a/packages/editor/src/components/post-taxonomies/flat-term-selector.js b/packages/editor/src/components/post-taxonomies/flat-term-selector.js index 6ca6a0cf624b1..d2be11cb3804d 100644 --- a/packages/editor/src/components/post-taxonomies/flat-term-selector.js +++ b/packages/editor/src/components/post-taxonomies/flat-term-selector.js @@ -2,7 +2,6 @@ * External dependencies */ import { isEmpty, get, unescape as unescapeString, find, throttle, uniqBy, invoke } from 'lodash'; -import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -13,6 +12,7 @@ import { FormTokenField } from '@wordpress/components'; import { withSelect, withDispatch } from '@wordpress/data'; import { compose } from '@wordpress/compose'; import apiFetch from '@wordpress/api-fetch'; +import { addQueryArgs } from '@wordpress/url'; /** * Module constants @@ -77,7 +77,9 @@ class FlatTermSelector extends Component { fetchTerms( params = {} ) { const { taxonomy } = this.props; const query = { ...DEFAULT_QUERY, ...params }; - const request = apiFetch( { path: `/wp/v2/${ taxonomy.rest_base }?${ httpBuildQuery( query ) }` } ); + const request = apiFetch( { + path: addQueryArgs( `/wp/v2/${ taxonomy.rest_base }`, query ), + } ); request.then( ( terms ) => { this.setState( ( state ) => ( { availableTerms: state.availableTerms.concat( @@ -116,7 +118,7 @@ class FlatTermSelector extends Component { if ( errorCode === 'term_exists' ) { // If the terms exist, fetch it instead of creating a new one. this.addRequest = apiFetch( { - path: `/wp/v2/${ taxonomy.rest_base }?${ stringify( { ...DEFAULT_QUERY, search: termName } ) }`, + path: addQueryArgs( `/wp/v2/${ taxonomy.rest_base }`, { ...DEFAULT_QUERY, search: termName } ), } ); return this.addRequest.then( ( searchResult ) => { return find( searchResult, ( result ) => isSameTermName( result.name, termName ) ); diff --git a/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js b/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js index 07f1fd62e875b..72c3c599a66ed 100644 --- a/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js +++ b/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js @@ -2,7 +2,6 @@ * External dependencies */ import { get, unescape as unescapeString, without, find, some, invoke } from 'lodash'; -import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -13,6 +12,7 @@ import { TreeSelect, withSpokenMessages, Button } from '@wordpress/components'; import { withSelect, withDispatch } from '@wordpress/data'; import { withInstanceId, compose } from '@wordpress/compose'; import apiFetch from '@wordpress/api-fetch'; +import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies @@ -121,7 +121,10 @@ class HierarchicalTermSelector extends Component { if ( errorCode === 'term_exists' ) { // search the new category created since last fetch this.addRequest = apiFetch( { - path: `/wp/v2/${ taxonomy.rest_base }?${ httpBuildQuery( { ...DEFAULT_QUERY, parent: formParent || 0, search: formName } ) }`, + path: addQueryArgs( + `/wp/v2/${ taxonomy.rest_base }`, + { ...DEFAULT_QUERY, parent: formParent || 0, search: formName } + ), } ); return this.addRequest .then( ( searchResult ) => { @@ -183,7 +186,9 @@ class HierarchicalTermSelector extends Component { if ( ! taxonomy ) { return; } - this.fetchRequest = apiFetch( { path: `/wp/v2/${ taxonomy.rest_base }?${ httpBuildQuery( DEFAULT_QUERY ) }` } ); + this.fetchRequest = apiFetch( { + path: addQueryArgs( `/wp/v2/${ taxonomy.rest_base }`, DEFAULT_QUERY ), + } ); this.fetchRequest.then( ( terms ) => { // resolve const availableTermsTree = buildTermsTree( terms ); diff --git a/packages/editor/src/components/url-input/index.js b/packages/editor/src/components/url-input/index.js index 262fba88b9095..7bc750de8275f 100644 --- a/packages/editor/src/components/url-input/index.js +++ b/packages/editor/src/components/url-input/index.js @@ -4,7 +4,6 @@ import { throttle } from 'lodash'; import classnames from 'classnames'; import scrollIntoView from 'dom-scroll-into-view'; -import httpBuildQuery from 'http-build-query'; /** * WordPress dependencies @@ -17,6 +16,7 @@ import { Spinner, withSpokenMessages, Popover } from '@wordpress/components'; import { withInstanceId } from '@wordpress/compose'; import apiFetch from '@wordpress/api-fetch'; import deprecated from '@wordpress/deprecated'; +import { addQueryArgs } from '@wordpress/url'; // Since URLInput is rendered in the context of other inputs, but should be // considered a separate modal node, prevent keyboard events from propagating @@ -91,11 +91,11 @@ class URLInput extends Component { } ); const request = apiFetch( { - path: `/gutenberg/v1/search?${ httpBuildQuery( { + path: addQueryArgs( '/gutenberg/v1/search', { search: value, per_page: 20, type: 'post', - } ) }`, + } ), } ); request.then( ( posts ) => { diff --git a/packages/url/package.json b/packages/url/package.json index 4657460af92a2..8fe57dac8f85b 100644 --- a/packages/url/package.json +++ b/packages/url/package.json @@ -19,7 +19,8 @@ "main": "build/index.js", "module": "build-module/index.js", "dependencies": { - "@babel/runtime": "^7.0.0-beta.52" + "@babel/runtime": "^7.0.0-beta.52", + "qs": "^6.5.2s" }, "publishConfig": { "access": "public" diff --git a/packages/url/src/index.js b/packages/url/src/index.js index 0463a306f0dca..7b8204c369def 100644 --- a/packages/url/src/index.js +++ b/packages/url/src/index.js @@ -1,7 +1,8 @@ /** * External dependencies */ -import { parse, format } from 'url'; +import { parse as parseURL, format as stringifyURL } from 'url'; +import { parse, stringify } from 'qs'; const EMAIL_REGEXP = /^(mailto:)?[a-z0-9._%+-]+@[a-z0-9][a-z0-9.-]*\.[a-z]{2,63}$/i; const USABLE_HREF_REGEXP = /^(?:[a-z]+:|#|\?|\.|\/)/i; @@ -15,11 +16,10 @@ const USABLE_HREF_REGEXP = /^(?:[a-z]+:|#|\?|\.|\/)/i; * @return {string} Updated URL */ export function addQueryArgs( url, args ) { - const parsedURL = parse( url, true ); - const query = { ...parsedURL.query, ...args }; - delete parsedURL.search; + const parsedURL = parseURL( url ); + const query = { ...parse( parsedURL.query ), ...args }; - return format( { ...parsedURL, query } ); + return stringifyURL( { ...parsedURL, search: stringify( query ) } ); } /** diff --git a/packages/url/src/test/index.test.js b/packages/url/src/test/index.test.js index 63a7a587db77e..0bd8b81abe446 100644 --- a/packages/url/src/test/index.test.js +++ b/packages/url/src/test/index.test.js @@ -24,6 +24,13 @@ describe( 'addQueryArgs', () => { expect( addQueryArgs( url, args ) ).toBe( 'https://andalouses.example/beach?night=false&sun=true&sand=false' ); } ); + + test( 'should update args to an URL with array parameters', () => { + const url = 'https://andalouses.example/beach?time[]=10&time[]=11'; + const args = { beach: [ 'sand', 'rock' ] }; + + expect( decodeURI( addQueryArgs( url, args ) ) ).toBe( 'https://andalouses.example/beach?time[0]=10&time[1]=11&beach[0]=sand&beach[1]=rock' ); + } ); } ); describe( 'prependHTTP', () => {