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

Add REST API endpoint to replace AJAX callback #33

Merged
merged 23 commits into from
Sep 12, 2019

Conversation

johnwatkins0
Copy link
Member

@johnwatkins0 johnwatkins0 commented Aug 11, 2019

This is intended to resolve #14 . It depends on #29 (Merged)

Description of the Change

REST endpoint

The current version of the plugin uses an AJAX handler to save the metadata that determines whether a post will be tweeted and whether the default autotweet content will be overridden.

This update removes the AJAX handler in favor of a custom REST endpoint. The endpoint is simple, accepting a POST request with three required fields (post ID, whether autotweet is enabled, and override content). Permission on the REST endpoint is set to check that the current user can edit the post. After the data is successfully saved, the REST endpoint sends back details about the data that was saved.

I also updated the JS file that handles the autotweet feature in the classic editor. It's now hooked up the REST endpoint instead of the AJAX endpoint.

Post meta updates

While setting up the REST endpoint, I did a minor refactor of some details around how post meta is handled. There was already a get_autotweet_meta function that wraps get_post_meta with a prefix on the meta key, and I created equivalents for updating and deleting meta. I also renamed some of the meta-related constants and used them in several places where the values were hardcoded. And I used the meta keys as the required parameters in the REST endpoint for clarity and consistency.

**Note: ** I changed the meta prefix to use autotweet instead of auto-tweet, and changed one of the meta keys to make it more descriptive. This will break backward-compatibility if the current version of this plugin is being used anywhere. Could that be an issue? If so, I will switch it back to what it was.

What this does not do

Although I modified the existing JS file to use the REST endpoint instead of AJAX -- and some ESLinting was applied to the file -- I stopped there. That JS file could easily be refactored to remove the jQuery dependency and use ES6 (via the Webpack config), but I considered that out of scope.

Alternative designs

I considered adding autotweet-related metadata as REST fields, eliminating the need for the REST endpoint. But with that approach, the autotweet data would be included in all REST responses for the supported posts, which probably isn't useful anywhere outside this plugin's narrow set of features.

Verification Process

Confirm that the integration with the classic editor still works as expected. It now runs through the REST API rather than AJAX.

Checklist:

  • I have read the CONTRIBUTING document.
  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my change.
  • All new and existing tests passed.

@@ -3,93 +3,87 @@
*
* @todo soooo much dependency :facepalm:
Copy link
Member Author

Choose a reason for hiding this comment

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

Most of this file's changeset is ESLint.

@@ -3,93 +3,87 @@
*
* @todo soooo much dependency :facepalm:
*/
(function ($) {
( function( $ ) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Intentionally not removing the jQuery dependency for this PR.


/**
* The meta prefix that all meta related keys should have
*/
const META_PREFIX = 'tenup-auto-tweet';
const META_PREFIX = 'tenup-autotweet';
Copy link
Member Author

@johnwatkins0 johnwatkins0 Aug 11, 2019

Choose a reason for hiding this comment

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

OK that this would break backward-compatibility?

Choose a reason for hiding this comment

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

probably not, leave as is

includes/rest.php Outdated Show resolved Hide resolved
@jeffpaul jeffpaul added the type:enhancement New feature or request. label Aug 12, 2019
@jeffpaul jeffpaul added this to the 1.0.0 milestone Aug 12, 2019
@johnwatkins0 johnwatkins0 changed the base branch from feature/webpack-build-process to develop August 13, 2019 02:12
@adamsilverstein
Copy link

**Note: ** I changed the meta prefix to use autotweet instead of auto-tweet, and changed one of the meta keys to make it more descriptive. This will break backward-compatibility if the current version of this plugin is being used anywhere. Could that be an issue? If so, I will switch it back to what it was.

This might be an issue, this is already in active use on 9to5.

Copy link

@adamsilverstein adamsilverstein left a comment

Choose a reason for hiding this comment

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

Thanks for your work here @johnwatkins0! I left some feedback, let me know if you have any questions. Will also test locally.

$post_id = intval( get_the_ID() );

if ( empty( $post_id ) ) {
$post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0; // phpcs:disable WordPress.Security.NonceVerification.Recommended

Choose a reason for hiding this comment

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

Can you document the reason for ignoring the nonce check here? See adamsilverstein/wp-post-meta-revisions#58 (comment) for format

'post_id': adminTUAT.postId,
'text': $tweetText.val(),
'type': event.type
$.ajax( adminAutotweet.restUrl, {

Choose a reason for hiding this comment

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

should we consider changing this to wp.apiFetch?

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 would be nice, but I believe for WP 4.9 we'd have to install it as an NPM dependency and bundle it with this file. Which is not a big lift at all.

Or is there another best practice for using WP5 JS packages in WP4?

Choose a reason for hiding this comment

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

it is tiny so we could bundle, however wp externalizes already so we can bundle a standalone wp.apiFetch file for 4.9 (either a copy from core or we can build on the fly from the npm package which is what core/gb and site kit do).

once we include the script in the plugin, we register the script if its not already registered, then add api-fetch as a dependency for our script. (we do this with a slew of scripts for Site Kit - https://github.com/google/site-kit-wp/blob/7d4aac9711a1174dc76acfad601c3fcd50a0baca/includes/Core/Assets/Assets.php#L627)... already registerd check: https://github.com/google/site-kit-wp/blob/develop/includes/Core/Assets/Script.php#L59

Copy link
Member Author

Choose a reason for hiding this comment

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

Nice, thanks for the links. I'll give that a shot.

@@ -134,25 +60,32 @@ function save_tweet_meta( $post_id ) {
// Check check.
if (
! isset( $_POST['tenup_auto_tweet_meta_nonce'] ) ||
! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['tenup_auto_tweet_meta_nonce'] ) ), 'tenup_auto_tweet_meta_fields' ) ||
! wp_verify_nonce(

Choose a reason for hiding this comment

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

can we drop this nonce check since the rest api is already performing a nonce check?

Choose a reason for hiding this comment

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

This nonce check can now be removed.

Copy link
Member Author

Choose a reason for hiding this comment

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

I have one more commit in progress -- got delayed by client work in the middle of these updates 😄

Choose a reason for hiding this comment

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

👍 I will patiently await your updates!

assets/js/admin-auto_tweet.js Outdated Show resolved Hide resolved
$.ajax( adminAutotweet.restUrl, {
beforeSend: function( xhr ) {
pendingStatus();
xhr.setRequestHeader( 'X-WP-Nonce', adminAutotweet.nonce );

Choose a reason for hiding this comment

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

if we do use wp.apiFetch I think it handles the nonce already?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup!

$id = isset( $request->get_attributes()['id'] ) ? $request->get_attributes()['id'] : null;
}

if ( ! is_int( $id ) || 1 > $id ) {

Choose a reason for hiding this comment

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

same

return false;
}

return current_user_can( 'edit_post', $id );

Choose a reason for hiding this comment

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

sanitization is missing? in sanitize_callback cast this as an int

function update_post_autotweet_meta( $request ) {
$post_id = $request['id'] ? $request['id'] : null;

if ( empty( $post_id ) ) {

Choose a reason for hiding this comment

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

move to validate_callback


$params = $request->get_params();

$sanitized_tweet_body = trim( sanitize_text_field( wp_unslash( $params[ TWEET_BODY_KEY ] ) ) );

Choose a reason for hiding this comment

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

sanitize the parameter in sanitize_callback

*
* @sincd 1.0.0
*/
class TestRest extends WP_UnitTestCase {

Choose a reason for hiding this comment

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

Nice!

@adamsilverstein adamsilverstein self-requested a review August 15, 2019 20:50
Copy link

@adamsilverstein adamsilverstein left a comment

Choose a reason for hiding this comment

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

Requested changes :)

@johnwatkins0
Copy link
Member Author

Thanks, @adamsilverstein -- great feedback! I'll follow up within the next few days.

data[adminAutotweet.enableAutotweetKey] = status;
data[adminAutotweet.tweetBodyKey] = $tweetText.val();

wp.apiFetch(

Choose a reason for hiding this comment

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

excellent!


});
};
).catch( onRequestFail );

Choose a reason for hiding this comment

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

We may want to look for errors in the then handler as well, we should do some testing around different errors: eg network error, blocked request (rest disabled) etc. See https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch


if ( empty( $post_id ) ) {
$post_id = intval(
filter_input( INPUT_GET, 'post', FILTER_SANITIZE_NUMBER_INT ) // Filter removes all characters except digits.

Choose a reason for hiding this comment

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

nice

@johnwatkins0
Copy link
Member Author

johnwatkins0 commented Aug 23, 2019

@adamsilverstein . I've pushed a few commits, and this is ready for another round of review. Let me know if you see anything else.

Summary of recent commits:

  1. dafbba7 Restored backward-compatible meta keys
  2. a3f1de9 Added more robust validation/sanitization of meta fields saved both via REST and via save_post. Refactored so that REST and save_post share a single function that saves data (previously it was implemented in two places).
  3. a3f1de9 Added a bit of extra error handling within apiFetch for when the response is not ok. I resisted the urge to go deep into this because I'm not certain at this point what errors to account for and how to do so. That could be a later task. For now, in the non-Gutenberg JS when apiFetch is not ok the user will see something like

Screen Shot 2019-08-22 at 10 15 25 PM

which feels like a WIP but is probably better than nothing. Let me know if you have suggestions.
  1. eb075f8 Removed a debug REST response mistakenly left in 🤦‍♂

Thank you for all your feedback! I'm looking forward to getting into Gutenberg metabox next.

function onRequestFail( error ) {
var errorText = '';
if ( 'statusText' in error && 'status' in error ) {
errorText = adminAutotweet.errorText + ' ' + error.status + ': ' + error.statusText;

Choose a reason for hiding this comment

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

This can use a template literal:

Suggested change
errorText = adminAutotweet.errorText + ' ' + error.status + ': ' + error.statusText;
errorText = `${ adminAutotweet.errorText } ${ error.status }: ${ error.statusText }`;

Copy link
Member Author

Choose a reason for hiding this comment

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

@adamsilverstein This JS isn't transpiled -- is browser support an issue if we use a template literal? https://caniuse.com/#feat=template-literals

/**
* Updates autotweet-related post metadata by prefixing the passed key.
*
* @param int $id Post ID.

Choose a reason for hiding this comment

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

parameter descriptions should be aligned

* @param int $id Post ID.
* @param string $key Autotweet meta key.
* @param mixed $value The meta value to save.
* @return mixed meta_id if the meta doesn't exist, otherwise returns true on success and false on failure.

Choose a reason for hiding this comment

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

Capitalize "If"

@adamsilverstein
Copy link

This looks great @johnwatkins0 ! I left a little bit of final feedback.

One thing I noticed when testing is that images are not sent with tweets - possibly because of the image I am testing with. I know this worked at some point in the past because I see images in my test tweets (https://twitter.com/adamtest110/). This was pulling from the featured image. We can address this in a follow up PR

@johnwatkins0
Copy link
Member Author

Thanks, @adamsilverstein . Addressed those last bits of feedback in those last two commits, and re-requesting review now.

@johnwatkins0 johnwatkins0 mentioned this pull request Aug 27, 2019
6 tasks
Copy link

@adamsilverstein adamsilverstein left a comment

Choose a reason for hiding this comment

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

Fantastic work @johnwatkins0 - thank you!

@johnwatkins0 johnwatkins0 merged commit 0fa2ca6 into develop Sep 12, 2019
@jeffpaul jeffpaul deleted the feature/rest-endpoint branch June 30, 2020 19:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:enhancement New feature or request.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add REST API endpoint to replace wp_ajax_tenup_auto_tweet [3 hrs.]
3 participants