From 65240a5cf2e23e2bc96792fb4f846223205f7337 Mon Sep 17 00:00:00 2001 From: Miguel Peixe Date: Tue, 28 Jan 2025 16:46:20 -0300 Subject: [PATCH 01/13] feat(content-distribution): partial payload --- includes/class-content-distribution.php | 76 +++++++++++++++++-- .../class-incoming-post.php | 31 +++++++- .../class-outgoing-post.php | 37 +++++++++ .../test-incoming-post.php | 47 ++++++++++++ .../test-outgoing-post.php | 12 +++ 5 files changed, 192 insertions(+), 11 deletions(-) diff --git a/includes/class-content-distribution.php b/includes/class-content-distribution.php index 6075878..36bd4e4 100644 --- a/includes/class-content-distribution.php +++ b/includes/class-content-distribution.php @@ -30,7 +30,7 @@ class Content_Distribution { * * @var array Post IDs to update. */ - private static $queued_post_updates = []; + private static $queued_distributions = []; /** * Initialize this class and register hooks @@ -77,22 +77,53 @@ public static function register_data_event_actions() { Data_Events::register_action( 'network_incoming_post_inserted' ); } + /** + * Queue post distribution to run on PHP shutdown. + * + * @param int $post_id The post ID. + * @param null|string $post_data_key The post data key to update. + * Default is null (entire post payload). + * + * @return void + */ + public static function queue_post_distribution( $post_id, $post_data_key = null ) { + // Bail if the post is already queued for a full update. + if ( isset( self::$queued_distributions[ $post_id ] ) && self::$queued_distributions[ $post_id ] === true ) { + return; + } + + // Queue for a full post update. + if ( empty( $post_data_key ) ) { + self::$queued_distributions[ $post_id ] = true; + return; + } + + // Queue for a partial update. + if ( ! isset( self::$queued_distributions[ $post_id ] ) ) { + self::$queued_distributions[ $post_id ] = []; + } + self::$queued_distributions[ $post_id ][] = $post_data_key; + } + /** * Distribute queued posts. */ public static function distribute_queued_posts() { - if ( empty( self::$queued_post_updates ) ) { + if ( empty( self::$queued_distributions ) ) { return; } - $post_ids = array_unique( self::$queued_post_updates ); - foreach ( $post_ids as $post_id ) { + foreach ( self::$queued_distributions as $post_id => $post_data_keys ) { $post = get_post( $post_id ); if ( ! $post ) { continue; } - self::distribute_post( $post ); + if ( is_array( $post_data_keys ) ) { + self::distribute_post_partial( $post, array_unique( $post_data_keys ) ); + } else { + self::distribute_post( $post ); + } } - self::$queued_post_updates = []; + self::$queued_distributions = []; } /** @@ -170,7 +201,7 @@ public static function handle_postmeta_update( $meta_id, $object_id, $meta_key ) ) { return; } - self::$queued_post_updates[] = $object_id; + self::queue_post_distribution( $post->ID, 'post_meta' ); } /** @@ -191,7 +222,7 @@ public static function handle_post_updated( $post ) { if ( ! self::is_post_distributed( $post ) ) { return; } - self::$queued_post_updates[] = $post->ID; + self::queue_post_distribution( $post->ID ); } /** @@ -388,4 +419,33 @@ public static function distribute_post( $post, $status_on_create = 'draft' ) { update_post_meta( $post->ID, self::PAYLOAD_HASH_META, $payload_hash ); } } + + /** + * Trigger a partial post distribution. + * + * @param WP_Post|Outgoing_Post|int $post The post object or ID. + * @param string[] $post_data_keys The post data key to update. + * + * @return void|WP_Error The error if the payload is invalid. + */ + public static function distribute_post_partial( $post, $post_data_keys ) { + if ( ! class_exists( 'Newspack\Data_Events' ) ) { + return; + } + if ( is_string( $post_data_keys ) ) { + $post_data_keys = [ $post_data_keys ]; + } + if ( $post instanceof Outgoing_Post ) { + $distributed_post = $post; + } else { + $distributed_post = self::get_distributed_post( $post ); + } + if ( $distributed_post ) { + $payload = $distributed_post->get_partial_payload( $post_data_keys ); + if ( is_wp_error( $payload ) ) { + return $payload; + } + Data_Events::dispatch( 'network_post_updated', $payload ); + } + } } diff --git a/includes/content-distribution/class-incoming-post.php b/includes/content-distribution/class-incoming-post.php index 6824a02..0e89e6f 100644 --- a/includes/content-distribution/class-incoming-post.php +++ b/includes/content-distribution/class-incoming-post.php @@ -87,9 +87,6 @@ public function __construct( $payload ) { throw new \InvalidArgumentException( esc_html( $error->get_error_message() ) ); } - $this->payload = $payload; - $this->network_post_id = $payload['network_post_id']; - if ( ! $post ) { $post = $this->query_post(); } @@ -98,6 +95,21 @@ public function __construct( $payload ) { $this->ID = $post->ID; $this->post = $post; } + + // Handle partial payload. + if ( ! empty( $payload['partial'] ) ) { + if ( ! $this->ID ) { + throw new \InvalidArgumentException( esc_html( __( 'Partial payload requires an existing post.', 'newspack-network' ) ) ); + } + + $current_post_data = $this->get_post_payload()['post_data']; + $payload['post_data'] = array_merge( $current_post_data, $payload['post_data'] ); + + unset( $payload['partial'] ); + } + + $this->payload = $payload; + $this->network_post_id = $payload['network_post_id']; } /** @@ -394,6 +406,19 @@ protected function update_payload( $payload ) { return $error; } + /** + * Handle partial payload + */ + if ( ! empty( $payload['partial'] ) ) { + $current_payload = $this->get_post_payload(); + if ( ! $this->ID ) { + self::log( 'Partial payload requires an existing post.' ); + return new WP_Error( 'partial_payload', __( 'Partial payload requires an existing post.', 'newspack-network' ) ); + } + $payload['post_data'] = array_merge( $current_payload['post_data'], $payload['post_data'] ); + unset( $payload['partial'] ); + } + // Do not update if network post ID mismatches. if ( $this->network_post_id !== $payload['network_post_id'] ) { return new WP_Error( 'mismatched_post_id', __( 'Mismatched post ID.', 'newspack-network' ) ); diff --git a/includes/content-distribution/class-outgoing-post.php b/includes/content-distribution/class-outgoing-post.php index c51537f..3ce75ce 100644 --- a/includes/content-distribution/class-outgoing-post.php +++ b/includes/content-distribution/class-outgoing-post.php @@ -228,6 +228,43 @@ public function get_payload( $status_on_create = 'draft' ) { ]; } + /** + * Get a partial payload for distribution. + * + * @param string[] $post_data_keys Keys in the post_data array to include in + * the partial payload. + * + * @return array|WP_Error The partial payload or WP_Error if any of the keys were not found. + */ + public function get_partial_payload( $post_data_keys ) { + if ( is_string( $post_data_keys ) ) { + $post_data_keys = [ $post_data_keys ]; + } + + $payload = $this->get_payload(); + foreach ( $post_data_keys as $post_data_key ) { + if ( ! isset( $payload['post_data'][ $post_data_key ] ) ) { + return new WP_Error( 'key_not_found', __( 'Key not found in payload.', 'newspack-network' ) ); + } + } + + // Mark the payload as partial. + $payload['partial'] = true; + + $post_data = []; + foreach ( $post_data_keys as $post_data_key ) { + $post_data[ $post_data_key ] = $payload['post_data'][ $post_data_key ]; + } + + // Always add the date and modified date to the partial payload. + $post_data['date_gmt'] = $payload['post_data']['date_gmt']; + $post_data['modified_gmt'] = $payload['post_data']['modified_gmt']; + + $payload['post_data'] = $post_data; + + return $payload; + } + /** * Get the processed post content for distribution. * diff --git a/tests/unit-tests/content-distribution/test-incoming-post.php b/tests/unit-tests/content-distribution/test-incoming-post.php index efbcbe1..43aac98 100644 --- a/tests/unit-tests/content-distribution/test-incoming-post.php +++ b/tests/unit-tests/content-distribution/test-incoming-post.php @@ -481,4 +481,51 @@ public function test_status_on_create() { $this->incoming_post->insert( $payload ); $this->assertSame( 'draft', get_post_status( $post_id ) ); } + + /** + * Test partial post payload update. + */ + public function test_partial_payload_insert() { + $payload = $this->get_sample_payload(); + + $post_id = $this->incoming_post->insert( $payload ); + + // Update the post payload with a partial update. + $payload['partial'] = true; + $payload['post_data'] = [ + 'title' => 'Updated Title', + 'date_gmt' => $payload['post_data']['date_gmt'], + 'modified_gmt' => $payload['post_data']['modified_gmt'], + ]; + + $this->incoming_post->insert( $payload ); + + // Assert that the post title was updated and the content was not. + $this->assertSame( 'Updated Title', get_the_title( $post_id ) ); + $this->assertSame( 'Content', get_post_field( 'post_content', $post_id ) ); + } + + /** + * Test partial post pyload update on instantiating. + */ + public function test_partial_payload_instantiating() { + $payload = $this->get_sample_payload(); + + $this->incoming_post->insert( $payload ); + + // Make the payload a partial. + $payload['partial'] = true; + $payload['post_data'] = [ + 'title' => 'Updated Title', + 'date_gmt' => $payload['post_data']['date_gmt'], + 'modified_gmt' => $payload['post_data']['modified_gmt'], + ]; + + $incoming_post = new Incoming_Post( $payload ); + $incoming_post->insert(); + + // Assert that the post title was updated and the content was not. + $this->assertSame( 'Updated Title', get_the_title( $post_id ) ); + $this->assertSame( 'Content', get_post_field( 'post_content', $post_id ) ); + } } diff --git a/tests/unit-tests/content-distribution/test-outgoing-post.php b/tests/unit-tests/content-distribution/test-outgoing-post.php index 3b48fb4..7213b7e 100644 --- a/tests/unit-tests/content-distribution/test-outgoing-post.php +++ b/tests/unit-tests/content-distribution/test-outgoing-post.php @@ -197,4 +197,16 @@ public function test_ignored_taxonomies() { $payload = $this->outgoing_post->get_payload(); $this->assertTrue( empty( $payload['post_data']['taxonomy'][ $taxonomy ] ) ); } + + /** + * Test get partial payload. + */ + public function test_get_partial_payload() { + $partial_payload = $this->outgoing_post->get_partial_payload( 'post_meta' ); + + $this->assertArrayHasKey( 'post_meta', $partial_payload['post_data'] ); + $this->assertArrayHasKey( 'date_gmt', $partial_payload['post_data'] ); + $this->assertArrayHasKey( 'modified_gmt', $partial_payload['post_data'] ); + $this->assertArrayNotHasKey( 'title', $partial_payload['post_data'] ); + } } From 8cd95575fc7e5a4b7234d47d6fb5426abfe2f6c5 Mon Sep 17 00:00:00 2001 From: Miguel Peixe Date: Tue, 28 Jan 2025 16:56:53 -0300 Subject: [PATCH 02/13] fix: typo --- includes/class-content-distribution.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-content-distribution.php b/includes/class-content-distribution.php index 36bd4e4..8c66dac 100644 --- a/includes/class-content-distribution.php +++ b/includes/class-content-distribution.php @@ -201,7 +201,7 @@ public static function handle_postmeta_update( $meta_id, $object_id, $meta_key ) ) { return; } - self::queue_post_distribution( $post->ID, 'post_meta' ); + self::queue_post_distribution( $object_id, 'post_meta' ); } /** From 31afc86778bc4f066ace3bc040fe12977aca96c5 Mon Sep 17 00:00:00 2001 From: Miguel Peixe Date: Tue, 28 Jan 2025 16:57:19 -0300 Subject: [PATCH 03/13] chore: restore for readbility --- includes/class-content-distribution.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-content-distribution.php b/includes/class-content-distribution.php index 8c66dac..36bd4e4 100644 --- a/includes/class-content-distribution.php +++ b/includes/class-content-distribution.php @@ -201,7 +201,7 @@ public static function handle_postmeta_update( $meta_id, $object_id, $meta_key ) ) { return; } - self::queue_post_distribution( $object_id, 'post_meta' ); + self::queue_post_distribution( $post->ID, 'post_meta' ); } /** From 143d197b105032d81ab201db098e633435603538 Mon Sep 17 00:00:00 2001 From: Miguel Peixe Date: Tue, 28 Jan 2025 17:16:45 -0300 Subject: [PATCH 04/13] fix: ensure payload is present for post query --- includes/class-content-distribution.php | 2 +- .../class-incoming-post.php | 67 ++++++++++++------- .../test-incoming-post.php | 5 +- 3 files changed, 45 insertions(+), 29 deletions(-) diff --git a/includes/class-content-distribution.php b/includes/class-content-distribution.php index 36bd4e4..a602079 100644 --- a/includes/class-content-distribution.php +++ b/includes/class-content-distribution.php @@ -424,7 +424,7 @@ public static function distribute_post( $post, $status_on_create = 'draft' ) { * Trigger a partial post distribution. * * @param WP_Post|Outgoing_Post|int $post The post object or ID. - * @param string[] $post_data_keys The post data key to update. + * @param string[] $post_data_keys The post data keys to update. * * @return void|WP_Error The error if the payload is invalid. */ diff --git a/includes/content-distribution/class-incoming-post.php b/includes/content-distribution/class-incoming-post.php index 0e89e6f..137b799 100644 --- a/includes/content-distribution/class-incoming-post.php +++ b/includes/content-distribution/class-incoming-post.php @@ -87,29 +87,26 @@ public function __construct( $payload ) { throw new \InvalidArgumentException( esc_html( $error->get_error_message() ) ); } + $this->payload = $payload; + $this->network_post_id = $payload['network_post_id']; + if ( ! $post ) { $post = $this->query_post(); } if ( $post ) { - $this->ID = $post->ID; - $this->post = $post; + $this->ID = $post->ID; + $this->post = $post; } // Handle partial payload. if ( ! empty( $payload['partial'] ) ) { - if ( ! $this->ID ) { - throw new \InvalidArgumentException( esc_html( __( 'Partial payload requires an existing post.', 'newspack-network' ) ) ); + $payload = $this->get_payload_from_partial( $payload ); + if ( is_wp_error( $payload ) ) { + throw new \InvalidArgumentException( esc_html( $payload->get_error_message() ) ); } - - $current_post_data = $this->get_post_payload()['post_data']; - $payload['post_data'] = array_merge( $current_post_data, $payload['post_data'] ); - - unset( $payload['partial'] ); + $this->payload = $payload; } - - $this->payload = $payload; - $this->network_post_id = $payload['network_post_id']; } /** @@ -185,6 +182,31 @@ protected function get_post_payload() { return get_post_meta( $this->ID, self::PAYLOAD_META, true ); } + /** + * Get payload from partial. + * + * @param array $payload The partial payload. + * + * @return array|WP_Error The full payload or WP_Error on failure. + */ + protected function get_payload_from_partial( $payload ) { + if ( ! $this->ID ) { + return new WP_Error( 'missing_post', __( 'Partial payload requires an existing post.', 'newspack-network' ) ); + } + + $current_payload = $this->get_post_payload(); + $current_payload_error = self::get_payload_error( $current_payload ); + if ( is_wp_error( $current_payload_error ) ) { + return $current_payload_error->get_error_message(); + } + + $payload['post_data'] = array_merge( $current_payload['post_data'], $payload['post_data'] ); + + unset( $payload['partial'] ); + + return $payload; + } + /** * Get the post's original site URL. * @@ -406,24 +428,19 @@ protected function update_payload( $payload ) { return $error; } - /** - * Handle partial payload - */ - if ( ! empty( $payload['partial'] ) ) { - $current_payload = $this->get_post_payload(); - if ( ! $this->ID ) { - self::log( 'Partial payload requires an existing post.' ); - return new WP_Error( 'partial_payload', __( 'Partial payload requires an existing post.', 'newspack-network' ) ); - } - $payload['post_data'] = array_merge( $current_payload['post_data'], $payload['post_data'] ); - unset( $payload['partial'] ); - } - // Do not update if network post ID mismatches. if ( $this->network_post_id !== $payload['network_post_id'] ) { return new WP_Error( 'mismatched_post_id', __( 'Mismatched post ID.', 'newspack-network' ) ); } + // Handle partial payload. + if ( ! empty( $payload['partial'] ) ) { + $payload = $this->get_payload_from_partial( $payload ); + if ( is_wp_error( $payload ) ) { + return $payload; + } + } + $this->payload = $payload; } diff --git a/tests/unit-tests/content-distribution/test-incoming-post.php b/tests/unit-tests/content-distribution/test-incoming-post.php index 43aac98..d77e82e 100644 --- a/tests/unit-tests/content-distribution/test-incoming-post.php +++ b/tests/unit-tests/content-distribution/test-incoming-post.php @@ -509,11 +509,10 @@ public function test_partial_payload_insert() { * Test partial post pyload update on instantiating. */ public function test_partial_payload_instantiating() { - $payload = $this->get_sample_payload(); - - $this->incoming_post->insert( $payload ); + $this->incoming_post->insert(); // Make the payload a partial. + $payload = $this->get_sample_payload(); $payload['partial'] = true; $payload['post_data'] = [ 'title' => 'Updated Title', From 667fef9ae89c7ad5d67ca1a6e9c8e89db6e999c1 Mon Sep 17 00:00:00 2001 From: Miguel Peixe Date: Tue, 28 Jan 2025 17:18:42 -0300 Subject: [PATCH 05/13] fix: restore $post_id for test --- tests/unit-tests/content-distribution/test-incoming-post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit-tests/content-distribution/test-incoming-post.php b/tests/unit-tests/content-distribution/test-incoming-post.php index d77e82e..e9d72d7 100644 --- a/tests/unit-tests/content-distribution/test-incoming-post.php +++ b/tests/unit-tests/content-distribution/test-incoming-post.php @@ -509,7 +509,7 @@ public function test_partial_payload_insert() { * Test partial post pyload update on instantiating. */ public function test_partial_payload_instantiating() { - $this->incoming_post->insert(); + $post_id = $this->incoming_post->insert(); // Make the payload a partial. $payload = $this->get_sample_payload(); From 01534806a69be8254637811afe99b2993f5c932a Mon Sep 17 00:00:00 2001 From: Miguel Peixe Date: Tue, 28 Jan 2025 17:20:00 -0300 Subject: [PATCH 06/13] chore: standardize test --- .../content-distribution/test-incoming-post.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/unit-tests/content-distribution/test-incoming-post.php b/tests/unit-tests/content-distribution/test-incoming-post.php index e9d72d7..03c7c7b 100644 --- a/tests/unit-tests/content-distribution/test-incoming-post.php +++ b/tests/unit-tests/content-distribution/test-incoming-post.php @@ -483,14 +483,13 @@ public function test_status_on_create() { } /** - * Test partial post payload update. + * Test partial post payload on insert. */ public function test_partial_payload_insert() { - $payload = $this->get_sample_payload(); - - $post_id = $this->incoming_post->insert( $payload ); + $post_id = $this->incoming_post->insert(); - // Update the post payload with a partial update. + // Make the payload a partial. + $payload = $this->get_sample_payload(); $payload['partial'] = true; $payload['post_data'] = [ 'title' => 'Updated Title', @@ -506,9 +505,9 @@ public function test_partial_payload_insert() { } /** - * Test partial post pyload update on instantiating. + * Test partial post payload on instantiation. */ - public function test_partial_payload_instantiating() { + public function test_partial_payload_instantiation() { $post_id = $this->incoming_post->insert(); // Make the payload a partial. From f58bb23a090dd1def1c049a86a8ae1ac0878490c Mon Sep 17 00:00:00 2001 From: Miguel Peixe Date: Tue, 28 Jan 2025 17:53:39 -0300 Subject: [PATCH 07/13] test: queue post distribution --- includes/class-content-distribution.php | 7 +++++ .../test-content-distribution.php | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/includes/class-content-distribution.php b/includes/class-content-distribution.php index a602079..5f3beea 100644 --- a/includes/class-content-distribution.php +++ b/includes/class-content-distribution.php @@ -105,6 +105,13 @@ public static function queue_post_distribution( $post_id, $post_data_key = null self::$queued_distributions[ $post_id ][] = $post_data_key; } + /** + * Get queued post distributions. + */ + public static function get_queued_distributions() { + return self::$queued_distributions; + } + /** * Distribute queued posts. */ diff --git a/tests/unit-tests/content-distribution/test-content-distribution.php b/tests/unit-tests/content-distribution/test-content-distribution.php index 748339e..cb4cd7f 100644 --- a/tests/unit-tests/content-distribution/test-content-distribution.php +++ b/tests/unit-tests/content-distribution/test-content-distribution.php @@ -7,6 +7,7 @@ namespace Test\Content_Distribution; +use Newspack_Network\Content_Distribution as Content_Distribution_Class; use Newspack_Network\Content_Distribution\Outgoing_Post; use Newspack_Network\Hub\Node as Hub_Node; @@ -64,4 +65,29 @@ public function test_update_distributed_post_meta() { $result = update_post_meta( $post_id, Outgoing_Post::DISTRIBUTED_POST_META, [ 'https://node.test', 'https://other-node.test' ] ); $this->assertNotFalse( $result ); } + + /** + * Test queue post distribution. + */ + public function test_queue_post_distribution() { + $post_id = $this->factory->post->create(); + + // Queue post meta for distribution. + Content_Distribution_Class::queue_post_distribution( $post_id, 'post_meta' ); + $queue = Content_Distribution_Class::get_queued_distributions(); + $this->assertArrayHasKey( $post_id, $queue ); + $this->assertSame( [ 'post_meta' ], $queue[ $post_id ] ); + + // Queue full post for distribution. + Content_Distribution_Class::queue_post_distribution( $post_id ); + $queue = Content_Distribution_Class::get_queued_distributions(); + $this->assertArrayHasKey( $post_id, $queue ); + $this->assertTrue( $queue[ $post_id ] ); + + // Queue another attribute for distribution. + $queue = Content_Distribution_Class::get_queued_distributions( $post_id, 'taxonomy' ); + $this->assertArrayHasKey( $post_id, $queue ); + // Assert that the post is still queued for full distribution. + $this->assertTrue( $queue[ $post_id ] ); + } } From d84ded00ad9b18e7ec80944a73b6b01194120d6b Mon Sep 17 00:00:00 2001 From: Miguel Peixe Date: Tue, 28 Jan 2025 17:59:01 -0300 Subject: [PATCH 08/13] test: increase coverage for outgoing_post --- .../test-outgoing-post.php | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/tests/unit-tests/content-distribution/test-outgoing-post.php b/tests/unit-tests/content-distribution/test-outgoing-post.php index 7213b7e..1b4cf8a 100644 --- a/tests/unit-tests/content-distribution/test-outgoing-post.php +++ b/tests/unit-tests/content-distribution/test-outgoing-post.php @@ -204,9 +204,29 @@ public function test_ignored_taxonomies() { public function test_get_partial_payload() { $partial_payload = $this->outgoing_post->get_partial_payload( 'post_meta' ); - $this->assertArrayHasKey( 'post_meta', $partial_payload['post_data'] ); - $this->assertArrayHasKey( 'date_gmt', $partial_payload['post_data'] ); - $this->assertArrayHasKey( 'modified_gmt', $partial_payload['post_data'] ); + $payload = $this->outgoing_post->get_payload(); + $this->assertSame( $payload['network_post_id'], $partial_payload['network_post_id'] ); + $this->assertSame( $payload['post_data']['post_meta'], $partial_payload['post_data']['post_meta'] ); + $this->assertSame( $payload['post_data']['date_gmt'], $partial_payload['post_data']['date_gmt'] ); + $this->assertSame( $payload['post_data']['modified_gmt'], $partial_payload['post_data']['modified_gmt'] ); + $this->assertArrayNotHasKey( 'title', $partial_payload['post_data'] ); + $this->assertArrayNotHasKey( 'content', $partial_payload['post_data'] ); + $this->assertArrayNotHasKey( 'taxonomy', $partial_payload['post_data'] ); + } + + /** + * Test get partial payload multiple keys. + */ + public function test_get_partial_payload_multiple_keys() { + $partial_payload = $this->outgoing_post->get_partial_payload( [ 'post_meta', 'taxonomy' ] ); + + $payload = $this->outgoing_post->get_payload(); + $this->assertSame( $payload['network_post_id'], $partial_payload['network_post_id'] ); + $this->assertSame( $payload['post_data']['post_meta'], $partial_payload['post_data']['post_meta'] ); + $this->assertSame( $payload['post_data']['taxonomy'], $partial_payload['post_data']['taxonomy'] ); + $this->assertSame( $payload['post_data']['date_gmt'], $partial_payload['post_data']['date_gmt'] ); + $this->assertSame( $payload['post_data']['modified_gmt'], $partial_payload['post_data']['modified_gmt'] ); $this->assertArrayNotHasKey( 'title', $partial_payload['post_data'] ); + $this->assertArrayNotHasKey( 'content', $partial_payload['post_data'] ); } } From 278e54a79c30feb2a541e88b8c84fd5f7159e179 Mon Sep 17 00:00:00 2001 From: Miguel Peixe Date: Tue, 28 Jan 2025 18:03:04 -0300 Subject: [PATCH 09/13] test: fix queue post distribution test --- .../content-distribution/test-content-distribution.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit-tests/content-distribution/test-content-distribution.php b/tests/unit-tests/content-distribution/test-content-distribution.php index cb4cd7f..99a7d16 100644 --- a/tests/unit-tests/content-distribution/test-content-distribution.php +++ b/tests/unit-tests/content-distribution/test-content-distribution.php @@ -81,12 +81,12 @@ public function test_queue_post_distribution() { // Queue full post for distribution. Content_Distribution_Class::queue_post_distribution( $post_id ); $queue = Content_Distribution_Class::get_queued_distributions(); - $this->assertArrayHasKey( $post_id, $queue ); + // Assert that the post is queued for full distribution (= true). $this->assertTrue( $queue[ $post_id ] ); // Queue another attribute for distribution. - $queue = Content_Distribution_Class::get_queued_distributions( $post_id, 'taxonomy' ); - $this->assertArrayHasKey( $post_id, $queue ); + Content_Distribution_Class::queue_post_distribution( $post_id, 'post_meta' ); + $queue = Content_Distribution_Class::get_queued_distributions(); // Assert that the post is still queued for full distribution. $this->assertTrue( $queue[ $post_id ] ); } From 51fa9851224e82f35735ccfa9e7941d6cac98234 Mon Sep 17 00:00:00 2001 From: Miguel Peixe Date: Tue, 28 Jan 2025 19:02:01 -0300 Subject: [PATCH 10/13] test: assert payload partial property --- includes/class-content-distribution.php | 4 +++- tests/unit-tests/content-distribution/test-outgoing-post.php | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/includes/class-content-distribution.php b/includes/class-content-distribution.php index 5f3beea..73f5a8a 100644 --- a/includes/class-content-distribution.php +++ b/includes/class-content-distribution.php @@ -51,7 +51,9 @@ public static function init() { add_filter( 'newspack_webhooks_request_priority', [ __CLASS__, 'webhooks_request_priority' ], 10, 2 ); add_filter( 'update_post_metadata', [ __CLASS__, 'maybe_short_circuit_distributed_meta' ], 10, 4 ); add_action( 'wp_after_insert_post', [ __CLASS__, 'handle_post_updated' ] ); - add_action( 'updated_postmeta', [ __CLASS__, 'handle_postmeta_update' ], 10, 3 ); + add_action( 'updated_post_meta', [ __CLASS__, 'handle_postmeta_update' ], 10, 3 ); + add_action( 'added_post_meta', [ __CLASS__, 'handle_postmeta_update' ], 10, 3 ); + add_action( 'deleted_post_meta', [ __CLASS__, 'handle_postmeta_update' ], 10, 3 ); add_action( 'before_delete_post', [ __CLASS__, 'handle_post_deleted' ] ); add_action( 'newspack_network_incoming_post_inserted', [ __CLASS__, 'handle_incoming_post_inserted' ], 10, 3 ); diff --git a/tests/unit-tests/content-distribution/test-outgoing-post.php b/tests/unit-tests/content-distribution/test-outgoing-post.php index 1b4cf8a..f48f4fd 100644 --- a/tests/unit-tests/content-distribution/test-outgoing-post.php +++ b/tests/unit-tests/content-distribution/test-outgoing-post.php @@ -205,6 +205,7 @@ public function test_get_partial_payload() { $partial_payload = $this->outgoing_post->get_partial_payload( 'post_meta' ); $payload = $this->outgoing_post->get_payload(); + $this->assertTrue( $payload['partial'] ); $this->assertSame( $payload['network_post_id'], $partial_payload['network_post_id'] ); $this->assertSame( $payload['post_data']['post_meta'], $partial_payload['post_data']['post_meta'] ); $this->assertSame( $payload['post_data']['date_gmt'], $partial_payload['post_data']['date_gmt'] ); @@ -221,6 +222,7 @@ public function test_get_partial_payload_multiple_keys() { $partial_payload = $this->outgoing_post->get_partial_payload( [ 'post_meta', 'taxonomy' ] ); $payload = $this->outgoing_post->get_payload(); + $this->assertTrue( $payload['partial'] ); $this->assertSame( $payload['network_post_id'], $partial_payload['network_post_id'] ); $this->assertSame( $payload['post_data']['post_meta'], $partial_payload['post_data']['post_meta'] ); $this->assertSame( $payload['post_data']['taxonomy'], $partial_payload['post_data']['taxonomy'] ); From d50d94ce32c6b9986f251b485cb238614042fa07 Mon Sep 17 00:00:00 2001 From: Miguel Peixe Date: Tue, 28 Jan 2025 19:03:41 -0300 Subject: [PATCH 11/13] test: fix typo --- tests/unit-tests/content-distribution/test-outgoing-post.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit-tests/content-distribution/test-outgoing-post.php b/tests/unit-tests/content-distribution/test-outgoing-post.php index f48f4fd..017bcb8 100644 --- a/tests/unit-tests/content-distribution/test-outgoing-post.php +++ b/tests/unit-tests/content-distribution/test-outgoing-post.php @@ -205,7 +205,7 @@ public function test_get_partial_payload() { $partial_payload = $this->outgoing_post->get_partial_payload( 'post_meta' ); $payload = $this->outgoing_post->get_payload(); - $this->assertTrue( $payload['partial'] ); + $this->assertTrue( $partial_payload['partial'] ); $this->assertSame( $payload['network_post_id'], $partial_payload['network_post_id'] ); $this->assertSame( $payload['post_data']['post_meta'], $partial_payload['post_data']['post_meta'] ); $this->assertSame( $payload['post_data']['date_gmt'], $partial_payload['post_data']['date_gmt'] ); @@ -222,7 +222,7 @@ public function test_get_partial_payload_multiple_keys() { $partial_payload = $this->outgoing_post->get_partial_payload( [ 'post_meta', 'taxonomy' ] ); $payload = $this->outgoing_post->get_payload(); - $this->assertTrue( $payload['partial'] ); + $this->assertTrue( $partial_payload['partial'] ); $this->assertSame( $payload['network_post_id'], $partial_payload['network_post_id'] ); $this->assertSame( $payload['post_data']['post_meta'], $partial_payload['post_data']['post_meta'] ); $this->assertSame( $payload['post_data']['taxonomy'], $partial_payload['post_data']['taxonomy'] ); From 3ce6f40413d6e04f8332c16e5d8902187c22c73d Mon Sep 17 00:00:00 2001 From: Miguel Peixe Date: Tue, 28 Jan 2025 19:13:17 -0300 Subject: [PATCH 12/13] test: partial payload exception --- .../test-incoming-post.php | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/unit-tests/content-distribution/test-incoming-post.php b/tests/unit-tests/content-distribution/test-incoming-post.php index 03c7c7b..4686c8b 100644 --- a/tests/unit-tests/content-distribution/test-incoming-post.php +++ b/tests/unit-tests/content-distribution/test-incoming-post.php @@ -526,4 +526,24 @@ public function test_partial_payload_instantiation() { $this->assertSame( 'Updated Title', get_the_title( $post_id ) ); $this->assertSame( 'Content', get_post_field( 'post_content', $post_id ) ); } + + /** + * Test partial payload on missing post. + */ + public function test_partial_payload_missing_post() { + $payload = $this->get_sample_payload(); + + // Make the payload a partial. + $payload['partial'] = true; + $payload['post_data'] = [ + 'title' => 'Updated Title', + 'date_gmt' => $payload['post_data']['date_gmt'], + 'modified_gmt' => $payload['post_data']['modified_gmt'], + ]; + + // Assert that instantiating a partial payload will throw an exception. + $this->expectException( \InvalidArgumentException::class ); + $this->expectExceptionMessage( 'Partial payload requires an existing post.' ); + new Incoming_Post( $payload ); + } } From ac91081baaf6d1312071dbdddb85e8644e7a78c7 Mon Sep 17 00:00:00 2001 From: Miguel Peixe Date: Wed, 29 Jan 2025 09:11:18 -0300 Subject: [PATCH 13/13] fix: error return --- includes/content-distribution/class-incoming-post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/content-distribution/class-incoming-post.php b/includes/content-distribution/class-incoming-post.php index 137b799..675e8ec 100644 --- a/includes/content-distribution/class-incoming-post.php +++ b/includes/content-distribution/class-incoming-post.php @@ -197,7 +197,7 @@ protected function get_payload_from_partial( $payload ) { $current_payload = $this->get_post_payload(); $current_payload_error = self::get_payload_error( $current_payload ); if ( is_wp_error( $current_payload_error ) ) { - return $current_payload_error->get_error_message(); + return $current_payload_error; } $payload['post_data'] = array_merge( $current_payload['post_data'], $payload['post_data'] );