Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve serialising JSON to PHP-compatible query strings #7339

Merged

Conversation

chrisvanpatten
Copy link
Member

@chrisvanpatten chrisvanpatten commented Jun 17, 2018

Description

This PR replaces #7232 and fixes #7086.

How has this been tested?

Manually in Chrome and Safari on macOS.

Types of changes

  • Adds a dependency: vladzadvorny/http-build-query (link)
  • Uses http-build-query to generate a PHP-compatible query string of block attributes for the Block Renderer API call

Note that there is a case where http-build-query diverges from the PHP implementation of http_build_query, where unneeded & symbols can be added to a query string for empty array or object values, however it does not cause any issues (PHP simply ignores these empty parameters when parsing the query string) and I've reported the issue upstream.

The bug has already been fixed! I've updated to http-build-query 0.7.0 in the PR.

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.

encodeURIComponent( key ) + '=' + encodeURIComponent( paramValue );
} ).join( '&' );
getQueryStringFromAttributes( attributes ) {
return isEmpty( attributes.attributes ) ? '' : '&' + httpBuildQuery( attributes );
Copy link
Contributor

@dimofte dimofte Jun 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • attributes is the property of the function argument, not the argument itself ("object" was more appropriate)
  • & is beyond the responsibility of this function, it should be up to the caller to glue the query params to the url with ? or &

I suggest ditching the function and moving the line to fetch

@dimofte
Copy link
Contributor

dimofte commented Jun 18, 2018

In the PR referenced above, I've added some tests to the package http-build-query, relevant to issues #7086 and #7214 . Of course, we don't need to wait for that to be merged (the tests pass)

Copy link
Member

@danielbachhuber danielbachhuber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a point of sanity checking, can we include tests on this PR too? This way, if there are ever regressions in the upstream library, we'll have visibility into them here.

@dimofte
Copy link
Contributor

dimofte commented Jun 18, 2018

@danielbachhuber what do you mean, do you want to add the tests for http-build-query to gutengerg?
Testing the parent function, fetch is, imo, outside of the scope of this PR.

@danielbachhuber
Copy link
Member

what do you mean, do you want to add the tests for http-build-query to Gutenberg?

Yes please.

@danielbachhuber
Copy link
Member

Flagging @aduth for approval of adding the library dependency. I'm unsure of our policy on adding dependencies.

@chrisvanpatten
Copy link
Member Author

chrisvanpatten commented Jun 18, 2018

@danielbachhuber @aduth if a dependency isn’t viable we could copy the function with credit to the @wordpress/url package, perhaps? Not sure how that works from a license perspective but it seems like a good place for a utility like this.

@dimofte dimofte force-pushed the fix/ssr-api-url-serialisation branch from 4ae3563 to 6a4d3d2 Compare June 18, 2018 16:53
Copy link
Member

@danielbachhuber danielbachhuber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two more minor nits, sorry.

@@ -46,9 +47,10 @@ export class ServerSideRender extends Component {
if ( null !== this.state.response ) {
this.setState( { response: null } );
}
const { block, attributes = {} } = props;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second thought, I think attributes should default to null and we can continue to leave it optional.


const path = '/gutenberg/v1/block-renderer/' + block + '?context=edit&' + this.getQueryUrlFromObject( { attributes } );
const path = `/gutenberg/v1/block-renderer/${ block }?context=edit` +
( attributes ? '&' + httpBuildQuery( { attributes } ) : '' );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

attributes ? should be null !== attributes, per above.

@danielbachhuber
Copy link
Member

if a dependency isn’t viable we could copy the function with credit to the @wordpress/url package, perhaps?

Yes, we could copy with attribution.

I'm not opposed to including the dependency though. I simply haven't merged a pull request with a dependency, so I want to get a sanity check from someone who has.

@chrisvanpatten
Copy link
Member Author

@danielbachhuber @aduth I think we're ready for a new review! 🚀

@danielbachhuber danielbachhuber added the Framework Issues related to broader framework topics, especially as it relates to javascript label Jun 19, 2018
@@ -12,6 +12,7 @@ import {
} from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import apiRequest from '@wordpress/api-request';
import httpBuildQuery from 'http-build-query';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use querystring instead? It's already bundled in webpack and used by several packages in Gutenberg?

Copy link
Member Author

@chrisvanpatten chrisvanpatten Jun 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@youknowriad PHP has its own unique format for query strings of arrays/objects.

Here's a rough example showing how each method encodes the query string differently:

qs = require('querystring');
hbq = require('http-build-query');

const obj = { "array": [ "a", "b", "c" ] };

qs.stringify(obj);
// array=a&array=b&array=c

hbq(obj);
// array[0]=a&array[1]=b&array[2]=c

Because http-build-query is just re-implementing PHP's native http_build_query function, it's transforming into the format that PHP can properly decode. If the querystring version of that object was passed onto PHP, it would be decoded as array( 'array' => 'c' ) and drop the other values in the array.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested querystring-es3 locally but it doesn't pass our tests:

image

It appears the project is relatively abandoned too: https://github.com/SpainTrain/querystring-es3

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure there's an option in querystring to do the exact same thing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@youknowriad I don't believe it does. I also looked at querystringify which is also included in Gutenberg and also doesn't support this format.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok so discussed in Slack, it's fine to keep http-build-query but I'd appreciate if we don't rush into merging when there's unresolved comments.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd appreciate if we don't rush into merging when there's unresolved comments.

At the time I merged, it wasn't clear there were unresolved comments.

@@ -46,9 +47,10 @@ export class ServerSideRender extends Component {
if ( null !== this.state.response ) {
this.setState( { response: null } );
}
const { block, attributes = {} } = props;
const { block, attributes = null } = props;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to have a null attributes? I thought we always fallback to {}?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to have a null attributes?

Nope.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was based on this: #7339 (comment) but I'm happy to go either way :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danielbachhuber should I update this to use {} instead of null?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should I update this to use {} instead of null?

No. null is more precise.

@danielbachhuber danielbachhuber modified the milestones: 3.2, 3.1 Jun 19, 2018
@danielbachhuber danielbachhuber merged commit 74842d0 into WordPress:master Jun 19, 2018
@@ -0,0 +1,64 @@
import httpBuildQuery from 'http-build-query';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also why are we testing an external library?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better implementation in #7371


// The following tests are adapted to the behavior of http-build-query v0.7.0,
// to help mitigating possible regressions in the upstream library.
describe( 'http-build-query', function() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we test our own code interacting with this library instead? I don't see any value in having this kind of tests. We use dozens of external libraries and we never test them directly.

@gziolo
Copy link
Member

gziolo commented Jun 20, 2018

This PR also updated 1 000+ integrity sha values which makes package-lock.json to be updated locally with the clean master. What version of npm did you use and which platform? Trying to understand why it happens.

@gziolo
Copy link
Member

gziolo commented Jun 20, 2018

Never mind, rm -rf node_modules && npm install solved it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Framework Issues related to broader framework topics, especially as it relates to javascript
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Server side render encodes some attributes incorrectly
5 participants