From 3dde99bfcd25d0630fd573b2590176be26156567 Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Fri, 20 Sep 2024 11:13:25 -0400 Subject: [PATCH 01/23] add standalone jw player adapter and api logic --- src/adapters/class-jw-player.php | 106 ++++++++++++++++++++ src/api/class-jw-player-api.php | 161 +++++++++++++++++++++++++++++++ wp-video-sync.php | 3 + 3 files changed, 270 insertions(+) create mode 100644 src/adapters/class-jw-player.php create mode 100644 src/api/class-jw-player-api.php diff --git a/src/adapters/class-jw-player.php b/src/adapters/class-jw-player.php new file mode 100644 index 0000000..873fd18 --- /dev/null +++ b/src/adapters/class-jw-player.php @@ -0,0 +1,106 @@ +jw_player_api = $api; + $this->origin_modified_date = ! empty( $origin_modified_date ) ? $origin_modified_date : date( 'Y-m-d' ); + } + + /** + * Fetches the date of the last modification to the last batch of videos. + * + * @return ?DateTimeImmutable + */ + public function get_last_modified_date(): ?DateTimeImmutable { + return $this->last_modified_date ?? DateTimeImmutable::createFromFormat('Y-m-d', $this->origin_modified_date ); + } + + /** + * Sets the date of the last modification to the latest batch of videos. + * + * @param array $videos An array of videos and associated data. + * @return void + */ + public function set_last_modified_date( array $videos ): void { + if ( + ! empty( $videos ) + && isset( $videos[ count( $videos ) - 1 ]->last_modified ) + ) { + $last_modified_date = DateTimeImmutable::createFromFormat( DATE_W3C, $videos[ count( $videos ) - 1 ]->last_modified ); + + if ( $last_modified_date instanceof DateTimeImmutable ) { + $this->last_modified_date = $last_modified_date; + } + } + } + + /** + * Fetches videos from JW Player that were modified after the provided DateTime. + * + * @param DateTimeImmutable $updated_after Return videos modified after this date. + * @param int $batch_size The number of videos to fetch in each batch. + * + * @return stdClass[] An array of video data. + */ + public function get_videos( DateTimeImmutable $updated_after, int $batch_size ): array { + // Get the latest videos from JW Player. + $videos = $this->jw_player_api->request_latest_videos( + $updated_after->format( 'Y-m-d' ), + $batch_size + ); + + // Check for an API error. + if ( ! empty( $videos['error'] ) ) { + return []; + } + + // Attempt to set the last modified date. + $this->set_last_modified_date( $videos ); + + // Return the videos. + return ! empty( $videos ) ? $videos : []; + } +} diff --git a/src/api/class-jw-player-api.php b/src/api/class-jw-player-api.php new file mode 100644 index 0000000..c782f3e --- /dev/null +++ b/src/api/class-jw-player-api.php @@ -0,0 +1,161 @@ +api_key = $api_key; + $this->api_secret = $api_secret; + } + + /** + * Set the user agent in the request headers for identification purposes. + * + * @return string + */ + public function user_agent(): string { + global $wp_version; + + return 'WordPress/' . $wp_version . ' WPVideoSync/' . WP_VIDEO_SYNC_VERSION . ' PHP/' . phpversion(); + } + + /** + * Generate the request URL. + * + * @param string $last_modified_date The date of the last modification to the last batch of videos. + * @param int $batch_size The number of videos to fetch in each batch. + * + * @return string + */ + public function request_url( string $last_modified_date, int $batch_size ): string { + $request_url = $this->api_url . '/' . $this->api_key . '/media/'; + + return add_query_arg( + [ + 'q' => 'last_modified:[' . $last_modified_date . ' TO *]', + 'page' => 1, + 'sort' => 'last_modified:asc', + 'page_length' => $batch_size, + ], + $request_url + ); + } + + /** + * Generate the request arguments. + * + * @param string $type The type of request to make. + * + * @return array + */ + public function request_args( string $type = '' ): array { + $default_args = [ + 'user-agent' => $this->user_agent(), + 'headers' => [ + 'Authorization' => 'Bearer ' . $this->api_secret, + 'Content-Type' => 'application/json', + ], + ]; + + return 'vip' === $type + ? $default_args + : array_merge( $default_args, [ 'timeout' => 3 ] ); + } + + /** + * Parse the API response. + * + * @param mixed $api_response The API response. + * + * @return array + */ + public function parse_response( mixed $api_response ): array { + // Failed request expressed as a WP_Error. + if ( is_wp_error( $api_response ) ) { + return []; + } + + // Condition for when the response body is empty. + $response_body = wp_remote_retrieve_body( $api_response ); + + if ( empty( $response_body ) ) { + return []; + } + + // Assign the response object for further evaluation. + $response_object = json_decode( $response_body ); + + if ( 200 !== wp_remote_retrieve_response_code( $api_response ) ) { + return isset( $response_object->errors[0]->description ) + ? [ 'error' => $response_object->errors[0]->description ] + : []; + } + + return is_array( $response_object->media ) && ! empty( $response_object->media ) + ? $response_object->media + : []; + } + + /** + * Make the API request. + * + * @param string $updated_after The date of the last modification to the last batch of videos. + * @param int $batch_size The number of videos to fetch in each batch. + * + * @return array + */ + public function request_latest_videos( string $updated_after, int $batch_size ): array { + $request_url = $this->request_url( + $updated_after, + $batch_size + ); + + if ( function_exists( 'vip_safe_wp_remote_get' ) ) { + $api_request = vip_safe_wp_remote_get( + $request_url, + '', + 3, + 5, + 3, + $this->request_args( 'vip' ) + ); + } else { + $api_request = wp_remote_get( + $request_url, + $this->request_args() + ); + } + + return $this->parse_response( $api_request ); + } +} diff --git a/wp-video-sync.php b/wp-video-sync.php index 77d17f3..4af0a86 100644 --- a/wp-video-sync.php +++ b/wp-video-sync.php @@ -21,4 +21,7 @@ exit; } +// Define the plugin version. +define( 'WP_VIDEO_SYNC_VERSION', '1.7.2' ); + require_once __DIR__ . '/src/autoload.php'; From 8c57df8fe2b92b8bc5497527a4615c86649fbc94 Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Fri, 20 Sep 2024 14:12:02 -0400 Subject: [PATCH 02/23] correct and update plugin point version --- wp-video-sync.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wp-video-sync.php b/wp-video-sync.php index 4af0a86..556b6f1 100644 --- a/wp-video-sync.php +++ b/wp-video-sync.php @@ -3,7 +3,7 @@ * Plugin Name: WP Video Sync * Plugin URI: https://github.com/alleyinteractive/wp-video-sync * Description: Sync videos from a hosting provider to WordPress - * Version: 0.1.0 + * Version: 0.2.0 * Author: Alley * Author URI: https://github.com/alleyinteractive/wp-video-sync * Requires at least: 6.0 @@ -22,6 +22,6 @@ } // Define the plugin version. -define( 'WP_VIDEO_SYNC_VERSION', '1.7.2' ); +define( 'WP_VIDEO_SYNC_VERSION', '0.2.0' ); require_once __DIR__ . '/src/autoload.php'; From 31c3aebda652298fa9932c46136e2b9c51aea032 Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Mon, 23 Sep 2024 09:45:50 -0400 Subject: [PATCH 03/23] remove unnecessary origin modified date --- src/adapters/class-jw-player.php | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/adapters/class-jw-player.php b/src/adapters/class-jw-player.php index 873fd18..ecbeffe 100644 --- a/src/adapters/class-jw-player.php +++ b/src/adapters/class-jw-player.php @@ -31,22 +31,13 @@ class JW_Player implements Adapter { */ public JW_Player_API $jw_player_api; - /** - * The start date if the `last_modified_date` does not exist. - * - * @var string - */ - public string $origin_modified_date; - /** * Constructor. * * @param JW_Player_API $api Instance of the JW Player API object. - * @param string $origin_modified_date The date of the used if the `last_modified_date` does not exist. */ - public function __construct( JW_Player_API $api, string $origin_modified_date = '' ) { - $this->jw_player_api = $api; - $this->origin_modified_date = ! empty( $origin_modified_date ) ? $origin_modified_date : date( 'Y-m-d' ); + public function __construct( JW_Player_API $api ) { + $this->jw_player_api = $api; } /** @@ -55,7 +46,7 @@ public function __construct( JW_Player_API $api, string $origin_modified_date = * @return ?DateTimeImmutable */ public function get_last_modified_date(): ?DateTimeImmutable { - return $this->last_modified_date ?? DateTimeImmutable::createFromFormat('Y-m-d', $this->origin_modified_date ); + return $this->last_modified_date; } /** From 1bc61983bb65d03b1d3fa0d8f14acf8936110064 Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Mon, 23 Sep 2024 11:07:25 -0400 Subject: [PATCH 04/23] assign allowed null value to type declaration --- src/adapters/class-jw-player.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapters/class-jw-player.php b/src/adapters/class-jw-player.php index ecbeffe..3463f3d 100644 --- a/src/adapters/class-jw-player.php +++ b/src/adapters/class-jw-player.php @@ -22,7 +22,7 @@ class JW_Player implements Adapter { * * @var ?DateTimeImmutable */ - private ?DateTimeImmutable $last_modified_date; + private ?DateTimeImmutable $last_modified_date = null; /** * The JW Player API. From dcd3f22c453258be18a300b96781c9c70f2c970d Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Mon, 23 Sep 2024 11:39:17 -0400 Subject: [PATCH 05/23] refactor request arg retrieval for api requests --- src/api/class-jw-player-api.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/api/class-jw-player-api.php b/src/api/class-jw-player-api.php index c782f3e..c1e2319 100644 --- a/src/api/class-jw-player-api.php +++ b/src/api/class-jw-player-api.php @@ -79,17 +79,13 @@ public function request_url( string $last_modified_date, int $batch_size ): stri * @return array */ public function request_args( string $type = '' ): array { - $default_args = [ + return [ 'user-agent' => $this->user_agent(), 'headers' => [ 'Authorization' => 'Bearer ' . $this->api_secret, 'Content-Type' => 'application/json', ], ]; - - return 'vip' === $type - ? $default_args - : array_merge( $default_args, [ 'timeout' => 3 ] ); } /** @@ -147,12 +143,17 @@ public function request_latest_videos( string $updated_after, int $batch_size ): 3, 5, 3, - $this->request_args( 'vip' ) + $this->request_args() ); } else { $api_request = wp_remote_get( $request_url, - $this->request_args() + wp_parse_args( + $this->request_args(), + [ + 'timeout' => 3, + ] + ) ); } From 6e57ffb4161ff463713fb767f7bb0284430a3c88 Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Mon, 23 Sep 2024 16:42:01 -0400 Subject: [PATCH 06/23] refactor logic to make the api calls more abstract --- src/adapters/class-jw-player.php | 12 ++-- src/api/class-jw-player-api.php | 113 ++++++++++--------------------- src/api/class-request.php | 110 ++++++++++++++++++++++++++++++ src/interfaces/api-requester.php | 45 ++++++++++++ 4 files changed, 199 insertions(+), 81 deletions(-) create mode 100644 src/api/class-request.php create mode 100644 src/interfaces/api-requester.php diff --git a/src/adapters/class-jw-player.php b/src/adapters/class-jw-player.php index 3463f3d..d30d54a 100644 --- a/src/adapters/class-jw-player.php +++ b/src/adapters/class-jw-player.php @@ -8,6 +8,7 @@ namespace Alley\WP\WP_Video_Sync\Adapters; use Alley\WP\WP_Video_Sync\API\JW_Player_API; +use Alley\WP\WP_Video_Sync\API\Request; use Alley\WP\WP_Video_Sync\Interfaces\Adapter; use DateTimeImmutable; use stdClass; @@ -77,21 +78,24 @@ public function set_last_modified_date( array $videos ): void { * @return stdClass[] An array of video data. */ public function get_videos( DateTimeImmutable $updated_after, int $batch_size ): array { - // Get the latest videos from JW Player. - $videos = $this->jw_player_api->request_latest_videos( + // Set the request URL based on the arguments. + $this->jw_player_api->set_request_url( $updated_after->format( 'Y-m-d' ), $batch_size ); + // Perform the request. + $videos = ( new Request( $this->jw_player_api ) )->get(); + // Check for an API error. if ( ! empty( $videos['error'] ) ) { return []; } // Attempt to set the last modified date. - $this->set_last_modified_date( $videos ); + $this->set_last_modified_date( $videos['media'] ); // Return the videos. - return ! empty( $videos ) ? $videos : []; + return ! empty( $videos['media'] ) ? $videos['media'] : []; } } diff --git a/src/api/class-jw-player-api.php b/src/api/class-jw-player-api.php index c1e2319..ea2cdfd 100644 --- a/src/api/class-jw-player-api.php +++ b/src/api/class-jw-player-api.php @@ -7,7 +7,12 @@ namespace Alley\WP\WP_Video_Sync\API; -class JW_Player_API { +use Alley\WP\WP_Video_Sync\Interfaces\API_Requester; + +/** + * JW Player API. + */ +class JW_Player_API implements API_Requester { /** * The API URL. @@ -30,6 +35,13 @@ class JW_Player_API { */ public string $api_secret; + /** + * The request URL. + * + * @var string + */ + public string $request_url; + /** * Constructor. */ @@ -38,29 +50,16 @@ public function __construct( string $api_key, string $api_secret ) { $this->api_secret = $api_secret; } - /** - * Set the user agent in the request headers for identification purposes. - * - * @return string - */ - public function user_agent(): string { - global $wp_version; - - return 'WordPress/' . $wp_version . ' WPVideoSync/' . WP_VIDEO_SYNC_VERSION . ' PHP/' . phpversion(); - } - /** * Generate the request URL. * * @param string $last_modified_date The date of the last modification to the last batch of videos. * @param int $batch_size The number of videos to fetch in each batch. - * - * @return string */ - public function request_url( string $last_modified_date, int $batch_size ): string { + public function set_request_url( string $last_modified_date, int $batch_size ) { $request_url = $this->api_url . '/' . $this->api_key . '/media/'; - return add_query_arg( + $this->request_url = add_query_arg( [ 'q' => 'last_modified:[' . $last_modified_date . ' TO *]', 'page' => 1, @@ -72,15 +71,21 @@ public function request_url( string $last_modified_date, int $batch_size ): stri } /** - * Generate the request arguments. + * Get the request URL. * - * @param string $type The type of request to make. + * @return string + */ + public function get_request_url(): string { + return $this->request_url; + } + + /** + * Get the request arguments. * * @return array */ - public function request_args( string $type = '' ): array { + public function get_request_args(): array { return [ - 'user-agent' => $this->user_agent(), 'headers' => [ 'Authorization' => 'Bearer ' . $this->api_secret, 'Content-Type' => 'application/json', @@ -89,74 +94,28 @@ public function request_args( string $type = '' ): array { } /** - * Parse the API response. + * Parse the API error response. * - * @param mixed $api_response The API response. + * @param mixed $response_object The API response object. * * @return array */ - public function parse_response( mixed $api_response ): array { - // Failed request expressed as a WP_Error. - if ( is_wp_error( $api_response ) ) { - return []; - } - - // Condition for when the response body is empty. - $response_body = wp_remote_retrieve_body( $api_response ); - - if ( empty( $response_body ) ) { - return []; - } - - // Assign the response object for further evaluation. - $response_object = json_decode( $response_body ); - - if ( 200 !== wp_remote_retrieve_response_code( $api_response ) ) { - return isset( $response_object->errors[0]->description ) - ? [ 'error' => $response_object->errors[0]->description ] - : []; - } - - return is_array( $response_object->media ) && ! empty( $response_object->media ) - ? $response_object->media + public function parse_error( array $response_object ): array { + return isset( $response_object['errors'][0]->description ) + ? [ 'error' => $response_object['errors'][0]->description ] : []; } /** - * Make the API request. + * Parse the API successful response. * - * @param string $updated_after The date of the last modification to the last batch of videos. - * @param int $batch_size The number of videos to fetch in each batch. + * @param mixed $response_object The API response object. * * @return array */ - public function request_latest_videos( string $updated_after, int $batch_size ): array { - $request_url = $this->request_url( - $updated_after, - $batch_size - ); - - if ( function_exists( 'vip_safe_wp_remote_get' ) ) { - $api_request = vip_safe_wp_remote_get( - $request_url, - '', - 3, - 5, - 3, - $this->request_args() - ); - } else { - $api_request = wp_remote_get( - $request_url, - wp_parse_args( - $this->request_args(), - [ - 'timeout' => 3, - ] - ) - ); - } - - return $this->parse_response( $api_request ); + public function parse_success( array $response_object ): array { + return is_array( $response_object['media'] ) && ! empty( $response_object['media'] ) + ? [ 'media' => $response_object['media'] ] + : []; } } diff --git a/src/api/class-request.php b/src/api/class-request.php new file mode 100644 index 0000000..307beff --- /dev/null +++ b/src/api/class-request.php @@ -0,0 +1,110 @@ +api_requester = $api_requester; + + return $this; + } + + /** + * Set the user agent in the request headers for identification purposes. + * + * @return string + */ + public function user_agent(): string { + global $wp_version; + + return 'WordPress/' . $wp_version . ' WPVideoSync/' . WP_VIDEO_SYNC_VERSION . ' PHP/' . phpversion(); + } + + /** + * Get the request arguments. + * + * @return array + */ + public function get_request_args(): array { + $requester_args = $this->api_requester->get_request_args(); + + if ( empty( $requester_args['user-agent'] ) ) { + $requester_args['user-agent'] = $this->user_agent(); + } + + return apply_filters( 'wp_video_sync_request_args', $requester_args ); + } + + /** + * Parse the API response. + * + * @param mixed $response + * @return array + */ + private function parse_response( mixed $response ): array { + // Failed request expressed as a WP_Error. + if ( is_wp_error( $response ) || empty( $response ) ) { + return []; + } + + // Condition for when the response body is empty. + $response_body = wp_remote_retrieve_body( $response ); + + if ( empty( $response_body ) ) { + return []; + } + + // Assign the response object for further evaluation. + $response_object = (array) json_decode( $response_body ); + + // Explicitly state the results based on response code. + return 200 === wp_remote_retrieve_response_code( $response ) + ? $this->api_requester->parse_success( $response_object ) + : $this->api_requester->parse_error( $response_object ); + } + + /** + * Perform a GET request. + * + * @return array + */ + public function get(): array { + if ( function_exists( 'vip_safe_wp_remote_get' ) ) { + $api_request = vip_safe_wp_remote_get( + $this->api_requester->get_request_url(), + '', + 3, + 5, + 3, + $this->get_request_args() + ); + } else { + $api_request = wp_remote_get( + $this->api_requester->get_request_url(), + $this->get_request_args() + ); + } + + return $this->parse_response( $api_request ); + } +} diff --git a/src/interfaces/api-requester.php b/src/interfaces/api-requester.php new file mode 100644 index 0000000..a08635a --- /dev/null +++ b/src/interfaces/api-requester.php @@ -0,0 +1,45 @@ + Date: Tue, 24 Sep 2024 08:48:17 -0400 Subject: [PATCH 07/23] consolidate repeated logic and moved to a parent class --- src/adapters/class-jw-player-7-for-wp.php | 23 ++---------- src/adapters/class-jw-player.php | 45 ++++------------------ src/class-last-modified-date.php | 46 +++++++++++++++++++++++ src/interfaces/adapter.php | 6 --- 4 files changed, 57 insertions(+), 63 deletions(-) create mode 100644 src/class-last-modified-date.php diff --git a/src/adapters/class-jw-player-7-for-wp.php b/src/adapters/class-jw-player-7-for-wp.php index b3957e3..bb9c8df 100644 --- a/src/adapters/class-jw-player-7-for-wp.php +++ b/src/adapters/class-jw-player-7-for-wp.php @@ -8,28 +8,14 @@ namespace Alley\WP\WP_Video_Sync\Adapters; use Alley\WP\WP_Video_Sync\Interfaces\Adapter; +use Alley\WP\WP_Video_Sync\Last_Modified_Date; use DateTimeImmutable; use stdClass; /** * JW Player 7 for WP Adapter. Supports both the free and premium versions of the plugin. */ -class JW_Player_7_For_WP implements Adapter { - /** - * The date of the last modification to the last batch of videos. - * - * @var ?DateTimeImmutable - */ - private ?DateTimeImmutable $last_modified_date; - - /** - * Fetches the date of the last modification to the last batch of videos. - * - * @return ?DateTimeImmutable - */ - public function get_last_modified_date(): ?DateTimeImmutable { - return $this->last_modified_date; - } +class JW_Player_7_For_WP extends Last_Modified_Date implements Adapter { /** * Fetches videos from JW Player that were modified after the provided DateTime. @@ -54,10 +40,7 @@ public function get_videos( DateTimeImmutable $updated_after, int $batch_size ): // Attempt to set the last modified date. if ( isset( $videos[ count( $videos ) - 1 ]->last_modified ) ) { - $last_modified_date = DateTimeImmutable::createFromFormat( DATE_W3C, $videos[ count( $videos ) - 1 ]->last_modified ); - if ( $last_modified_date instanceof DateTimeImmutable ) { - $this->last_modified_date = $last_modified_date; - } + $this->set_last_modified_date( $videos[ count( $videos ) - 1 ]->last_modified ); } return $videos; diff --git a/src/adapters/class-jw-player.php b/src/adapters/class-jw-player.php index d30d54a..219718c 100644 --- a/src/adapters/class-jw-player.php +++ b/src/adapters/class-jw-player.php @@ -10,20 +10,14 @@ use Alley\WP\WP_Video_Sync\API\JW_Player_API; use Alley\WP\WP_Video_Sync\API\Request; use Alley\WP\WP_Video_Sync\Interfaces\Adapter; +use Alley\WP\WP_Video_Sync\Last_Modified_Date; use DateTimeImmutable; use stdClass; /** * JW Player Adapter. */ -class JW_Player implements Adapter { - - /** - * The date of the last modification to the last batch of videos. - * - * @var ?DateTimeImmutable - */ - private ?DateTimeImmutable $last_modified_date = null; +class JW_Player extends Last_Modified_Date implements Adapter { /** * The JW Player API. @@ -41,34 +35,6 @@ public function __construct( JW_Player_API $api ) { $this->jw_player_api = $api; } - /** - * Fetches the date of the last modification to the last batch of videos. - * - * @return ?DateTimeImmutable - */ - public function get_last_modified_date(): ?DateTimeImmutable { - return $this->last_modified_date; - } - - /** - * Sets the date of the last modification to the latest batch of videos. - * - * @param array $videos An array of videos and associated data. - * @return void - */ - public function set_last_modified_date( array $videos ): void { - if ( - ! empty( $videos ) - && isset( $videos[ count( $videos ) - 1 ]->last_modified ) - ) { - $last_modified_date = DateTimeImmutable::createFromFormat( DATE_W3C, $videos[ count( $videos ) - 1 ]->last_modified ); - - if ( $last_modified_date instanceof DateTimeImmutable ) { - $this->last_modified_date = $last_modified_date; - } - } - } - /** * Fetches videos from JW Player that were modified after the provided DateTime. * @@ -93,7 +59,12 @@ public function get_videos( DateTimeImmutable $updated_after, int $batch_size ): } // Attempt to set the last modified date. - $this->set_last_modified_date( $videos['media'] ); + if ( + ! empty( $videos['media'] ) + && isset( $videos['media'][ count( $videos['media'] ) - 1 ]->last_modified ) + ) { + $this->set_last_modified_date( $videos['media'][ count( $videos ) - 1 ]->last_modified ); + } // Return the videos. return ! empty( $videos['media'] ) ? $videos['media'] : []; diff --git a/src/class-last-modified-date.php b/src/class-last-modified-date.php new file mode 100644 index 0000000..5e19e41 --- /dev/null +++ b/src/class-last-modified-date.php @@ -0,0 +1,46 @@ +last_modified_date; + } + + /** + * Sets the date of the last modification based on the latest batch of videos. + * + * @param string $last_modified_date The date of the last modified video in the batch. + * @return void + */ + public function set_last_modified_date( string $last_modified_date = '' ): void { + $last_modified_date = DateTimeImmutable::createFromFormat( DATE_W3C, $last_modified_date ); + + if ( $last_modified_date instanceof \DateTimeImmutable ) { + $this->last_modified_date = $last_modified_date; + } + } +} diff --git a/src/interfaces/adapter.php b/src/interfaces/adapter.php index c660628..18ffeb4 100644 --- a/src/interfaces/adapter.php +++ b/src/interfaces/adapter.php @@ -14,12 +14,6 @@ * Defines an interface that all adapters must implement. */ interface Adapter { - /** - * Fetches the date of the last modification to the last batch of videos. - * - * @return ?DateTimeImmutable - */ - public function get_last_modified_date(): ?DateTimeImmutable; /** * Fetches videos from the provider that were modified after the provided DateTime. From 8d24cc1e7b9660b905a143c28a561f132f41d4fc Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Thu, 26 Sep 2024 08:00:03 -0400 Subject: [PATCH 08/23] address phpcs sniffs --- src/api/class-jw-player-api.php | 9 ++++++--- src/api/class-request.php | 9 +++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/api/class-jw-player-api.php b/src/api/class-jw-player-api.php index ea2cdfd..aa03512 100644 --- a/src/api/class-jw-player-api.php +++ b/src/api/class-jw-player-api.php @@ -44,6 +44,9 @@ class JW_Player_API implements API_Requester { /** * Constructor. + * + * @param string $api_key The API key. + * @param string $api_secret The API secret. */ public function __construct( string $api_key, string $api_secret ) { $this->api_key = $api_key; @@ -86,7 +89,7 @@ public function get_request_url(): string { */ public function get_request_args(): array { return [ - 'headers' => [ + 'headers' => [ 'Authorization' => 'Bearer ' . $this->api_secret, 'Content-Type' => 'application/json', ], @@ -96,7 +99,7 @@ public function get_request_args(): array { /** * Parse the API error response. * - * @param mixed $response_object The API response object. + * @param array $response_object The API response object. * * @return array */ @@ -109,7 +112,7 @@ public function parse_error( array $response_object ): array { /** * Parse the API successful response. * - * @param mixed $response_object The API response object. + * @param array $response_object The API response object. * * @return array */ diff --git a/src/api/class-request.php b/src/api/class-request.php index 307beff..ba2fadc 100644 --- a/src/api/class-request.php +++ b/src/api/class-request.php @@ -9,6 +9,9 @@ use Alley\WP\WP_Video_Sync\Interfaces\API_Requester; +/** + * Perform an API request. + */ class Request { /** @@ -25,8 +28,6 @@ class Request { */ public function __construct( API_Requester $api_requester ) { $this->api_requester = $api_requester; - - return $this; } /** @@ -58,7 +59,7 @@ public function get_request_args(): array { /** * Parse the API response. * - * @param mixed $response + * @param mixed $response The API response. * @return array */ private function parse_response( mixed $response ): array { @@ -99,7 +100,7 @@ public function get(): array { $this->get_request_args() ); } else { - $api_request = wp_remote_get( + $api_request = wp_remote_get( // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get $this->api_requester->get_request_url(), $this->get_request_args() ); From 1cfbed88d9666f095b8436b223b1e4d493a04bc0 Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Thu, 26 Sep 2024 10:58:28 -0400 Subject: [PATCH 09/23] address phpstan errors --- src/adapters/class-jw-player.php | 10 +++++++--- src/api/class-jw-player-api.php | 19 +++++++++++-------- src/api/class-request.php | 14 ++++++++++---- src/interfaces/adapter.php | 3 +-- src/interfaces/api-requester.php | 21 ++++++++++++--------- 5 files changed, 41 insertions(+), 26 deletions(-) diff --git a/src/adapters/class-jw-player.php b/src/adapters/class-jw-player.php index 219718c..56e55a5 100644 --- a/src/adapters/class-jw-player.php +++ b/src/adapters/class-jw-player.php @@ -12,7 +12,6 @@ use Alley\WP\WP_Video_Sync\Interfaces\Adapter; use Alley\WP\WP_Video_Sync\Last_Modified_Date; use DateTimeImmutable; -use stdClass; /** * JW Player Adapter. @@ -41,7 +40,7 @@ public function __construct( JW_Player_API $api ) { * @param DateTimeImmutable $updated_after Return videos modified after this date. * @param int $batch_size The number of videos to fetch in each batch. * - * @return stdClass[] An array of video data. + * @return array An array of video data objects. */ public function get_videos( DateTimeImmutable $updated_after, int $batch_size ): array { // Set the request URL based on the arguments. @@ -58,9 +57,14 @@ public function get_videos( DateTimeImmutable $updated_after, int $batch_size ): return []; } + // Validate the media property. + if ( ! is_array( $videos['media'] ) ) { + return []; + } + // Attempt to set the last modified date. if ( - ! empty( $videos['media'] ) + ! empty( $videos['media'][ count( $videos['media'] ) - 1 ] ) && isset( $videos['media'][ count( $videos['media'] ) - 1 ]->last_modified ) ) { $this->set_last_modified_date( $videos['media'][ count( $videos ) - 1 ]->last_modified ); diff --git a/src/api/class-jw-player-api.php b/src/api/class-jw-player-api.php index aa03512..0697e3a 100644 --- a/src/api/class-jw-player-api.php +++ b/src/api/class-jw-player-api.php @@ -59,7 +59,7 @@ public function __construct( string $api_key, string $api_secret ) { * @param string $last_modified_date The date of the last modification to the last batch of videos. * @param int $batch_size The number of videos to fetch in each batch. */ - public function set_request_url( string $last_modified_date, int $batch_size ) { + public function set_request_url( string $last_modified_date, int $batch_size ): void { $request_url = $this->api_url . '/' . $this->api_key . '/media/'; $this->request_url = add_query_arg( @@ -85,7 +85,7 @@ public function get_request_url(): string { /** * Get the request arguments. * - * @return array + * @return array> */ public function get_request_args(): array { return [ @@ -99,12 +99,14 @@ public function get_request_args(): array { /** * Parse the API error response. * - * @param array $response_object The API response object. + * @param array $response_object The API response object. * - * @return array + * @return array */ public function parse_error( array $response_object ): array { - return isset( $response_object['errors'][0]->description ) + return ! empty( $response_object['errors'] ) + && is_array($response_object['errors'] ) + && isset( $response_object['errors'][0]->description ) ? [ 'error' => $response_object['errors'][0]->description ] : []; } @@ -112,12 +114,13 @@ public function parse_error( array $response_object ): array { /** * Parse the API successful response. * - * @param array $response_object The API response object. + * @param array $response_object The API response object. * - * @return array + * @return array */ public function parse_success( array $response_object ): array { - return is_array( $response_object['media'] ) && ! empty( $response_object['media'] ) + return ! empty( $response_object['media'] ) + && is_array( $response_object['media'] ) ? [ 'media' => $response_object['media'] ] : []; } diff --git a/src/api/class-request.php b/src/api/class-request.php index ba2fadc..92c1433 100644 --- a/src/api/class-request.php +++ b/src/api/class-request.php @@ -44,7 +44,7 @@ public function user_agent(): string { /** * Get the request arguments. * - * @return array + * @return array */ public function get_request_args(): array { $requester_args = $this->api_requester->get_request_args(); @@ -53,6 +53,11 @@ public function get_request_args(): array { $requester_args['user-agent'] = $this->user_agent(); } + /** + * Allow the request arguments to be filtered before the request is made. + * + * @param array $requester_args The request arguments. + */ return apply_filters( 'wp_video_sync_request_args', $requester_args ); } @@ -60,11 +65,12 @@ public function get_request_args(): array { * Parse the API response. * * @param mixed $response The API response. - * @return array + * + * @return array */ private function parse_response( mixed $response ): array { // Failed request expressed as a WP_Error. - if ( is_wp_error( $response ) || empty( $response ) ) { + if ( is_wp_error( $response ) || empty( $response ) || ! is_array( $response ) ) { return []; } @@ -87,7 +93,7 @@ private function parse_response( mixed $response ): array { /** * Perform a GET request. * - * @return array + * @return array */ public function get(): array { if ( function_exists( 'vip_safe_wp_remote_get' ) ) { diff --git a/src/interfaces/adapter.php b/src/interfaces/adapter.php index 18ffeb4..2039b5c 100644 --- a/src/interfaces/adapter.php +++ b/src/interfaces/adapter.php @@ -8,7 +8,6 @@ namespace Alley\WP\WP_Video_Sync\Interfaces; use DateTimeImmutable; -use stdClass; /** * Defines an interface that all adapters must implement. @@ -21,7 +20,7 @@ interface Adapter { * @param DateTimeImmutable $updated_after Return videos modified after this date. * @param int $batch_size The number of videos to fetch in each batch. * - * @return stdClass[] An array of video data. Specific shape will be determined by the adapter. + * @return array An array of video data. Specific shape will be determined by the adapter. */ public function get_videos( DateTimeImmutable $updated_after, int $batch_size ): array; } diff --git a/src/interfaces/api-requester.php b/src/interfaces/api-requester.php index a08635a..22ff701 100644 --- a/src/interfaces/api-requester.php +++ b/src/interfaces/api-requester.php @@ -23,23 +23,26 @@ public function get_request_url(): string; /** * Get the arguments for the API request. * - * @return array + * @return array> */ public function get_request_args(): array; /** - * Parse a successful API response. + * Parse an error API response. + * + * @param array $response_object The API response object. * - * @param array $response_object The response object. - * @return array + * @return array */ - public function parse_success( array $response_object ): array; + public function parse_error( array $response_object ): array; /** - * Parse an error API response. + * Parse a successful API response. + * + * @param array $response_object The API response object. * - * @param array $response_object The response object. - * @return array + * @return array */ - public function parse_error( array $response_object ): array; + public function parse_success( array $response_object ): array; + } From 4a728e97833069afd8618d7da0229b1cf070c570 Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Thu, 26 Sep 2024 12:14:51 -0400 Subject: [PATCH 10/23] check for function existence. PHPStan not checking the parent class method for defined function --- src/class-sync-manager.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/class-sync-manager.php b/src/class-sync-manager.php index f80aef4..fa4a006 100644 --- a/src/class-sync-manager.php +++ b/src/class-sync-manager.php @@ -113,9 +113,11 @@ public function sync_videos(): void { } // Try to update the last sync time to the last modified time of the last video that was processed. - $next_last_modified = $this->adapter->get_last_modified_date(); - if ( $next_last_modified instanceof DateTimeImmutable ) { - update_option( self::LAST_SYNC_OPTION, $next_last_modified->format( DATE_W3C ), false ); + if ( method_exists( $this->adapter, 'get_last_modified_date' ) ) { + $next_last_modified = $this->adapter->get_last_modified_date(); + if ( $next_last_modified instanceof DateTimeImmutable ) { + update_option( self::LAST_SYNC_OPTION, $next_last_modified->format( DATE_W3C ), false ); + } } } From 400d6c696fffe60d00781273f005dda2c437abdf Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Thu, 26 Sep 2024 12:15:21 -0400 Subject: [PATCH 11/23] strict parameter definition to conform to the wp_remote_get parameter shape --- src/api/class-request.php | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/api/class-request.php b/src/api/class-request.php index 92c1433..639e575 100644 --- a/src/api/class-request.php +++ b/src/api/class-request.php @@ -106,9 +106,43 @@ public function get(): array { $this->get_request_args() ); } else { + // Get the base request arguments. + $custom_args = $this->get_request_args(); + + // Explicitly define the request arguments. + // NOTE: this is addressing an error stemming from PHPStan where the passed arguments to the `wp_remote_get()` were not accepted. + $request_args = []; + + // Request method. + if ( ! empty( $custom_args['method'] ) && is_string( $custom_args['method'] ) ) { + $request_args['method'] = $custom_args['method']; + } + + // Request headers. + if ( + ! empty( $custom_args['headers'] ) + && ( + is_string( $custom_args['headers'] ) + || is_array( $custom_args['headers'] ) + ) + ) { + $request_args['headers'] = $custom_args['headers']; + } + + // Request timeout. + if ( ! empty( $custom_args['timeout'] ) && is_float( $custom_args['timeout'] ) ) { + $request_args['timeout'] = $custom_args['timeout']; + } + + // Request user agent. + if ( ! empty( $custom_args['user-agent'] ) && is_string( $custom_args['user-agent'] ) ) { + $request_args['user-agent'] = $custom_args['user-agent']; + } + + // Perform the request. $api_request = wp_remote_get( // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get $this->api_requester->get_request_url(), - $this->get_request_args() + array_filter( $request_args ) ); } From 91d61c909c6da41b44e44fb27c6c1792b0d276eb Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Thu, 26 Sep 2024 12:19:47 -0400 Subject: [PATCH 12/23] correct formatting --- src/api/class-jw-player-api.php | 2 +- src/interfaces/api-requester.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/api/class-jw-player-api.php b/src/api/class-jw-player-api.php index 0697e3a..558c21c 100644 --- a/src/api/class-jw-player-api.php +++ b/src/api/class-jw-player-api.php @@ -105,7 +105,7 @@ public function get_request_args(): array { */ public function parse_error( array $response_object ): array { return ! empty( $response_object['errors'] ) - && is_array($response_object['errors'] ) + && is_array( $response_object['errors'] ) && isset( $response_object['errors'][0]->description ) ? [ 'error' => $response_object['errors'][0]->description ] : []; diff --git a/src/interfaces/api-requester.php b/src/interfaces/api-requester.php index 22ff701..b97b6b3 100644 --- a/src/interfaces/api-requester.php +++ b/src/interfaces/api-requester.php @@ -44,5 +44,4 @@ public function parse_error( array $response_object ): array; * @return array */ public function parse_success( array $response_object ): array; - } From 55c36fc6f21a23bfcd1ffb0977f0b3bae6b82804 Mon Sep 17 00:00:00 2001 From: Sean Fisher Date: Thu, 26 Sep 2024 15:13:06 -0400 Subject: [PATCH 13/23] Testing CI From 97b5f7b22a0e89996051b09285ac22755b5d5247 Mon Sep 17 00:00:00 2001 From: Sean Fisher Date: Thu, 26 Sep 2024 15:17:33 -0400 Subject: [PATCH 14/23] Enable debug mode --- phpunit.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 646bcb6..e60481b 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -16,7 +16,8 @@ - - + + + From 910760d56172c23e719120a3375628656e58f324 Mon Sep 17 00:00:00 2001 From: Sean Fisher Date: Thu, 26 Sep 2024 15:23:25 -0400 Subject: [PATCH 15/23] Fix action --- .github/workflows/all-pr-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/all-pr-tests.yml b/.github/workflows/all-pr-tests.yml index 0d7c7c1..c276b35 100644 --- a/.github/workflows/all-pr-tests.yml +++ b/.github/workflows/all-pr-tests.yml @@ -25,5 +25,5 @@ jobs: uses: alleyinteractive/action-test-php@develop with: php-version: ${{ matrix.php-version }} - wordpress-host: 'false' - wordpress-version: 'false' + wordpress-version: 'latest' + skip-wordpress-install: 'true' From b15d7a2e0114b6abd6277af01a62220396190098 Mon Sep 17 00:00:00 2001 From: Sean Fisher Date: Thu, 26 Sep 2024 15:25:02 -0400 Subject: [PATCH 16/23] Remove debug mode --- phpunit.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index e60481b..646bcb6 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -16,8 +16,7 @@ - - - + + From 0bb364a3e4d56fb25be62f3654db4067f7c87a95 Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Fri, 27 Sep 2024 09:14:30 -0400 Subject: [PATCH 17/23] add unit test for the JW Player Adapter integration --- tests/Feature/JWPlayer7ForWPAdapterTest.php | 68 +++++++++++++++++++++ tests/Feature/JWPlayerAdapterTest.php | 22 ++++--- 2 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 tests/Feature/JWPlayer7ForWPAdapterTest.php diff --git a/tests/Feature/JWPlayer7ForWPAdapterTest.php b/tests/Feature/JWPlayer7ForWPAdapterTest.php new file mode 100644 index 0000000..e6468ee --- /dev/null +++ b/tests/Feature/JWPlayer7ForWPAdapterTest.php @@ -0,0 +1,68 @@ +fake_request( 'https://api.jwplayer.com/v2/sites/example-api-key/media/*' ) + ->with_response_code( 200 ) + ->with_body( file_get_contents( __DIR__ . '/../Fixtures/jw-player-api-v2-media.json' ) ); + + // Create an instance of the adapter. + $sync_manager = Sync_Manager::init() + ->with_adapter( new JW_Player_7_For_WP() ) + ->with_callback( fn ( $video ) => self::factory()->post->create( + [ + 'post_title' => $video->metadata->title, + 'post_date' => DateTimeImmutable::createFromFormat( DATE_W3C, $video->created )->format( 'Y-m-d H:i:s' ), + 'post_modified' => DateTimeImmutable::createFromFormat( DATE_W3C, $video->last_modified )->format( 'Y-m-d H:i:s' ), + 'meta_input' => [ + 'jwplayer_id' => $video->id, + ], + ] + ) ); + + // Run the sync. + $sync_manager->sync_videos(); + + // Confirm that the sync was successful. + $video_query = new WP_Query( + [ + 'name' => 'example-video', + 'post_status' => 'publish', + 'post_type' => 'post', + ] + ); + $this->assertEquals( 1, $video_query->post_count ); + $this->assertEquals( 'Example Video', $video_query->posts[0]->post_title ); + $this->assertEquals( '2024-01-01 12:00:00', $video_query->posts[0]->post_date ); + $this->assertEquals( '2024-01-01 12:00:00', $video_query->posts[0]->post_date_gmt ); + $this->assertEquals( '2024-01-01 13:00:00', $video_query->posts[0]->post_modified ); + $this->assertEquals( '2024-01-01 13:00:00', $video_query->posts[0]->post_modified_gmt ); + $this->assertEquals( 'ABCD1234', get_post_meta( $video_query->posts[0]->ID, 'jwplayer_id', true ) ); + + // Ensure that the sync time was updated. + $this->assertEquals( '2024-01-01T13:00:00+00:00', get_option( Sync_Manager::LAST_SYNC_OPTION ) ); + } +} diff --git a/tests/Feature/JWPlayerAdapterTest.php b/tests/Feature/JWPlayerAdapterTest.php index bf0ea56..e0aa7f0 100644 --- a/tests/Feature/JWPlayerAdapterTest.php +++ b/tests/Feature/JWPlayerAdapterTest.php @@ -7,7 +7,8 @@ namespace Alley\WP\WP_Video_Sync\Tests\Feature; -use Alley\WP\WP_Video_Sync\Adapters\JW_Player_7_For_WP; +use Alley\WP\WP_Video_Sync\API\JW_Player_API; +use Alley\WP\WP_Video_Sync\Adapters\JW_Player; use Alley\WP\WP_Video_Sync\Sync_Manager; use Alley\WP\WP_Video_Sync\Tests\TestCase; use DateTimeImmutable; @@ -17,21 +18,23 @@ * A test suite for the JW Player adapter. */ class JWPlayerAdapterTest extends TestCase { + /** - * Tests the behavior of the adapter with the JW Player 7 for WP plugin (free and premium). + * Tests the behavior of the adapter with the JW Player. */ - public function test_jw_player_7_for_wp() { - // Fake the class that's used to make the API call. - require_once __DIR__ . '/../Mocks/JWPPP_Dashboard_API.php'; - + public function test_jw_player() { // Fake the API response for the first page of data. - $this->fake_request( 'https://api.jwplayer.com/v2/sites/example-api-key/media/*' ) + $this->fake_request( 'https://api.jwplayer.com/v2/sites/api_key/media*' ) ->with_response_code( 200 ) - ->with_body( file_get_contents( __DIR__ . '/../Fixtures/jw-player-api-v2-media.json' ) ); + ->with_file( __DIR__ . '/../Fixtures/jw-player-api-v2-media.json' ); // Create an instance of the adapter. $sync_manager = Sync_Manager::init() - ->with_adapter( new JW_Player_7_For_WP() ) + ->with_adapter( + new JW_Player( + new JW_Player_API( 'api_key', 'api_secret' ) + ) + ) ->with_callback( fn ( $video ) => self::factory()->post->create( [ 'post_title' => $video->metadata->title, @@ -54,6 +57,7 @@ public function test_jw_player_7_for_wp() { 'post_type' => 'post', ] ); + $this->assertEquals( 1, $video_query->post_count ); $this->assertEquals( 'Example Video', $video_query->posts[0]->post_title ); $this->assertEquals( '2024-01-01 12:00:00', $video_query->posts[0]->post_date ); From 020ded3da6c4add297cbf22a34d47dd7caa71d61 Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Tue, 1 Oct 2024 16:44:26 -0400 Subject: [PATCH 18/23] provide base README documentation outlining class methods --- README.md | 2 +- src/adapters/README.md | 28 ++++++++++++++++++++++++++++ src/api/README.md | 7 +++++++ src/interfaces/README.md | 7 +++++++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/adapters/README.md create mode 100644 src/api/README.md create mode 100644 src/interfaces/README.md diff --git a/README.md b/README.md index b3626aa..33a5776 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ use DateInterval; use DateTimeImmutable; use WP_Query; -add_action( 'plugins_loaded', function () => { +add_action( 'plugins_loaded', function () { $sync_manager = Sync_Manager::init() ->with_adapter( new JW_Player_7_For_WP() ) ->with_frequency( 'hourly' ) diff --git a/src/adapters/README.md b/src/adapters/README.md new file mode 100644 index 0000000..d717e45 --- /dev/null +++ b/src/adapters/README.md @@ -0,0 +1,28 @@ +## Class JW_Player + +This class will implement the base `Adapter` interface. Instantiating this class with a `JW_Player_API` object will allow the request to be made to get the latest videos to be synced. This class may be used to work with a theme's custom integration with JW Player. + +Example Integration: + +```shell +\Alley\WP\WP_Video_Sync\Sync_Manager::init() + ->with_adapter( + new \Alley\WP\WP_Video_Sync\Adapters\JW_Player( + new \Alley\WP\WP_Video_Sync\API\JW_Player_API( + [PROPERTY_ID], + [API_KEY] + ) + ) + ) + ->with_batch_size() + ->with_frequency() + ->with_callback( + function ( $video ) { + // Do something with the video. + } +); +``` + +## Class JW_Player_7_For_WP + +This class is to work with the JW Player 7 for WordPress plugin. diff --git a/src/api/README.md b/src/api/README.md new file mode 100644 index 0000000..28ac867 --- /dev/null +++ b/src/api/README.md @@ -0,0 +1,7 @@ +## Class JW_Player_API + +This class is used for constructing an object for making an API call to the JW Player API. Simply pass your API key and API Secret on instantiation. Please reference the interface `API_Requester` for the required methods. This object may be used to pass to the `JW_Player` adapter. + +## Class Request + +This class will handle the generation of the URL for performing an API request. The API response will call the `parse_response` method which will subsequently call the `parse_success()` or `parse_error()` method of the `API_Requester` object that is used to construct this object. diff --git a/src/interfaces/README.md b/src/interfaces/README.md new file mode 100644 index 0000000..ca627f8 --- /dev/null +++ b/src/interfaces/README.md @@ -0,0 +1,7 @@ +## Class Adapter + +Adhere to the Adapter interface to create a new adapter for syncing videos. + +## Class API_Request + +Adhere to the API_Request interface to provide the necesssary methods for making and processing an API request. From f54af24498b8986d79e1e7bc55e3fc7cbb962a8c Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Mon, 7 Oct 2024 10:19:01 -0400 Subject: [PATCH 19/23] implement php constructor property promotion --- src/adapters/class-jw-player.php | 13 ++----------- src/api/class-jw-player-api.php | 24 +++++------------------- src/api/class-request.php | 13 ++----------- 3 files changed, 9 insertions(+), 41 deletions(-) diff --git a/src/adapters/class-jw-player.php b/src/adapters/class-jw-player.php index 56e55a5..00470b0 100644 --- a/src/adapters/class-jw-player.php +++ b/src/adapters/class-jw-player.php @@ -18,21 +18,12 @@ */ class JW_Player extends Last_Modified_Date implements Adapter { - /** - * The JW Player API. - * - * @var JW_Player_API - */ - public JW_Player_API $jw_player_api; - /** * Constructor. * - * @param JW_Player_API $api Instance of the JW Player API object. + * @param JW_Player_API $jw_player_api Instance of the JW Player API object. */ - public function __construct( JW_Player_API $api ) { - $this->jw_player_api = $api; - } + public function __construct( public readonly JW_Player_API $jw_player_api ) {} /** * Fetches videos from JW Player that were modified after the provided DateTime. diff --git a/src/api/class-jw-player-api.php b/src/api/class-jw-player-api.php index 558c21c..1ed3da8 100644 --- a/src/api/class-jw-player-api.php +++ b/src/api/class-jw-player-api.php @@ -21,20 +21,6 @@ class JW_Player_API implements API_Requester { */ public string $api_url = 'https://api.jwplayer.com/v2/sites'; - /** - * The API public key. - * - * @var string - */ - public string $api_key; - - /** - * The API v2 secret key. - * - * @var string - */ - public string $api_secret; - /** * The request URL. * @@ -46,12 +32,12 @@ class JW_Player_API implements API_Requester { * Constructor. * * @param string $api_key The API key. - * @param string $api_secret The API secret. + * @param string $api_secret The API v2 secret key. */ - public function __construct( string $api_key, string $api_secret ) { - $this->api_key = $api_key; - $this->api_secret = $api_secret; - } + public function __construct( + public readonly string $api_key, + public readonly string $api_secret + ) {} /** * Generate the request URL. diff --git a/src/api/class-request.php b/src/api/class-request.php index 639e575..876180e 100644 --- a/src/api/class-request.php +++ b/src/api/class-request.php @@ -14,21 +14,12 @@ */ class Request { - /** - * The API requester. - * - * @var API_Requester - */ - public API_Requester $api_requester; - /** * Constructor. * - * @param API_Requester $api_requester The API requester. + * @param API_Requester $api_requester Instance of the API requester. */ - public function __construct( API_Requester $api_requester ) { - $this->api_requester = $api_requester; - } + public function __construct( public readonly API_Requester $api_requester ) {} /** * Set the user agent in the request headers for identification purposes. From e7e8ce0fb6442a9d403939e9d4fce8cd52ca9a7e Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Mon, 7 Oct 2024 10:19:42 -0400 Subject: [PATCH 20/23] ensure get_last_modified_date method is applied within adapter --- src/interfaces/adapter.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/interfaces/adapter.php b/src/interfaces/adapter.php index 2039b5c..f6069a9 100644 --- a/src/interfaces/adapter.php +++ b/src/interfaces/adapter.php @@ -14,6 +14,13 @@ */ interface Adapter { + /** + * Fetches the date of the last modification to the last batch of videos. + * + * @return ?DateTimeImmutable + */ + public function get_last_modified_date(): ?DateTimeImmutable; + /** * Fetches videos from the provider that were modified after the provided DateTime. * From 3ad4dd50f542345a6911a71f1e6a4f12419370e3 Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Mon, 7 Oct 2024 10:20:11 -0400 Subject: [PATCH 21/23] restrict setting last modified date to a DateTimeImmutable object --- src/class-last-modified-date.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/class-last-modified-date.php b/src/class-last-modified-date.php index 5e19e41..0fc2b5a 100644 --- a/src/class-last-modified-date.php +++ b/src/class-last-modified-date.php @@ -33,14 +33,10 @@ public function get_last_modified_date(): ?DateTimeImmutable { /** * Sets the date of the last modification based on the latest batch of videos. * - * @param string $last_modified_date The date of the last modified video in the batch. + * @param DateTimeImmutable $last_modified_date The date of the last modified video in the batch. * @return void */ - public function set_last_modified_date( string $last_modified_date = '' ): void { - $last_modified_date = DateTimeImmutable::createFromFormat( DATE_W3C, $last_modified_date ); - - if ( $last_modified_date instanceof \DateTimeImmutable ) { - $this->last_modified_date = $last_modified_date; - } + public function set_last_modified_date( DateTimeImmutable $last_modified_date ): void { + $this->last_modified_date = $last_modified_date; } } From 739708210a76ba8c0dddf83f979439786f108772 Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Mon, 7 Oct 2024 15:30:34 -0400 Subject: [PATCH 22/23] ensure last modified date is a DateTimeImmutable instance, encapsulate methods for the jwp api implementation --- src/adapters/class-jw-player-7-for-wp.php | 5 ++++- src/adapters/class-jw-player.php | 27 ++++++++++++++--------- src/api/class-jw-player-api.php | 23 ++++++++++++++++++- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/adapters/class-jw-player-7-for-wp.php b/src/adapters/class-jw-player-7-for-wp.php index bb9c8df..58434b4 100644 --- a/src/adapters/class-jw-player-7-for-wp.php +++ b/src/adapters/class-jw-player-7-for-wp.php @@ -40,7 +40,10 @@ public function get_videos( DateTimeImmutable $updated_after, int $batch_size ): // Attempt to set the last modified date. if ( isset( $videos[ count( $videos ) - 1 ]->last_modified ) ) { - $this->set_last_modified_date( $videos[ count( $videos ) - 1 ]->last_modified ); + $last_modified_date = DateTimeImmutable::createFromFormat( DATE_W3C, $videos[ count( $videos ) - 1 ]->last_modified ); + if ( $last_modified_date instanceof DateTimeImmutable ) { + $this->set_last_modified_date( $last_modified_date ); + } } return $videos; diff --git a/src/adapters/class-jw-player.php b/src/adapters/class-jw-player.php index 00470b0..b373558 100644 --- a/src/adapters/class-jw-player.php +++ b/src/adapters/class-jw-player.php @@ -8,7 +8,6 @@ namespace Alley\WP\WP_Video_Sync\Adapters; use Alley\WP\WP_Video_Sync\API\JW_Player_API; -use Alley\WP\WP_Video_Sync\API\Request; use Alley\WP\WP_Video_Sync\Interfaces\Adapter; use Alley\WP\WP_Video_Sync\Last_Modified_Date; use DateTimeImmutable; @@ -21,9 +20,13 @@ class JW_Player extends Last_Modified_Date implements Adapter { /** * Constructor. * - * @param JW_Player_API $jw_player_api Instance of the JW Player API object. + * @param string $api_key The API key. + * @param string $api_secret The API v2 secret key. */ - public function __construct( public readonly JW_Player_API $jw_player_api ) {} + public function __construct( + public readonly string $api_key, + public readonly string $api_secret + ) {} /** * Fetches videos from JW Player that were modified after the provided DateTime. @@ -34,15 +37,16 @@ public function __construct( public readonly JW_Player_API $jw_player_api ) {} * @return array An array of video data objects. */ public function get_videos( DateTimeImmutable $updated_after, int $batch_size ): array { - // Set the request URL based on the arguments. - $this->jw_player_api->set_request_url( - $updated_after->format( 'Y-m-d' ), + $videos = ( + new JW_Player_API( + $this->api_key, + $this->api_secret + ) + )->get_videos_after( + $updated_after, $batch_size ); - // Perform the request. - $videos = ( new Request( $this->jw_player_api ) )->get(); - // Check for an API error. if ( ! empty( $videos['error'] ) ) { return []; @@ -58,7 +62,10 @@ public function get_videos( DateTimeImmutable $updated_after, int $batch_size ): ! empty( $videos['media'][ count( $videos['media'] ) - 1 ] ) && isset( $videos['media'][ count( $videos['media'] ) - 1 ]->last_modified ) ) { - $this->set_last_modified_date( $videos['media'][ count( $videos ) - 1 ]->last_modified ); + $last_modified_date = DateTimeImmutable::createFromFormat( DATE_W3C, $videos['media'][ count( $videos['media'] ) - 1 ]->last_modified ); + if ( $last_modified_date instanceof DateTimeImmutable ) { + $this->set_last_modified_date( $last_modified_date ); + } } // Return the videos. diff --git a/src/api/class-jw-player-api.php b/src/api/class-jw-player-api.php index 1ed3da8..a2a17f2 100644 --- a/src/api/class-jw-player-api.php +++ b/src/api/class-jw-player-api.php @@ -8,11 +8,13 @@ namespace Alley\WP\WP_Video_Sync\API; use Alley\WP\WP_Video_Sync\Interfaces\API_Requester; +use Alley\WP\WP_Video_Sync\Last_Modified_Date; +use DateTimeImmutable; /** * JW Player API. */ -class JW_Player_API implements API_Requester { +class JW_Player_API extends Last_Modified_Date implements API_Requester { /** * The API URL. @@ -82,6 +84,25 @@ public function get_request_args(): array { ]; } + /** + * Fetches videos from JW Player that were modified after the provided DateTime. + * + * @param DateTimeImmutable $updated_after Return videos modified after this date. + * @param int $batch_size The number of videos to fetch in each batch. + * + * @return array An array of video data objects. + */ + public function get_videos_after( DateTimeImmutable $updated_after, int $batch_size ): array { + // Set the request URL based on the arguments. + $this->set_request_url( + $updated_after->format( 'Y-m-d' ), + $batch_size + ); + + // Perform the request. + return ( new Request( $this ) )->get(); + } + /** * Parse the API error response. * From 31a0135f130c2722ca22cded4a4dba4b290dd7fc Mon Sep 17 00:00:00 2001 From: Morgan Newcomb Date: Mon, 7 Oct 2024 15:33:13 -0400 Subject: [PATCH 23/23] update unit test to remove dependency class param --- tests/Feature/JWPlayerAdapterTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Feature/JWPlayerAdapterTest.php b/tests/Feature/JWPlayerAdapterTest.php index e0aa7f0..5e1aa07 100644 --- a/tests/Feature/JWPlayerAdapterTest.php +++ b/tests/Feature/JWPlayerAdapterTest.php @@ -32,7 +32,8 @@ public function test_jw_player() { $sync_manager = Sync_Manager::init() ->with_adapter( new JW_Player( - new JW_Player_API( 'api_key', 'api_secret' ) + 'api_key', + 'api_secret' ) ) ->with_callback( fn ( $video ) => self::factory()->post->create(