From 98f851e32f8f4231908ac8c2825aeb20931d355f Mon Sep 17 00:00:00 2001 From: ntsekouras Date: Mon, 14 Nov 2022 12:23:42 +0200 Subject: [PATCH] [Pattern Directory]: Add categories endpoint --- ...-rest-pattern-directory-controller-6-2.php | 123 +++++++++++++++++- ...rest-pattern-directory-controller-test.php | 45 +++++-- 2 files changed, 158 insertions(+), 10 deletions(-) diff --git a/lib/compat/wordpress-6.2/class-gutenberg-rest-pattern-directory-controller-6-2.php b/lib/compat/wordpress-6.2/class-gutenberg-rest-pattern-directory-controller-6-2.php index cc44c37b4d7c9c..c038460559021b 100644 --- a/lib/compat/wordpress-6.2/class-gutenberg-rest-pattern-directory-controller-6-2.php +++ b/lib/compat/wordpress-6.2/class-gutenberg-rest-pattern-directory-controller-6-2.php @@ -10,6 +10,125 @@ * Controller which provides REST endpoint for block patterns from wordpress.org/patterns. */ class Gutenberg_REST_Pattern_Directory_Controller_6_2 extends Gutenberg_REST_Pattern_Directory_Controller_6_0 { + /** + * Registers the necessary REST API routes. + * + * @since 5.8.0 + * @since 6.2.0 Added pattern directory categories endpoint. + */ + public function register_routes() { + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/categories', + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_pattern_categories' ), + 'permission_callback' => array( $this, 'get_items_permissions_check' ), + ) + ), + ); + + parent::register_routes(); + } + + /** + * Retrieve block patterns categories. + * + * @since 6.2.0 + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + public function get_pattern_categories( $request ) { + $api_url = 'http://api.wordpress.org/patterns/1.0/?categories'; + $transient_key = 'wp_remote_block_pattern_categories_' . md5( $api_url ); + + /** + * Use network-wide transient to improve performance. The locale is the only site + * configuration that affects the response, and it's included in the transient key. + */ + $raw_pattern_categories = get_site_transient( $transient_key ); + if ( ! $raw_pattern_categories ) { + if ( wp_http_supports( array( 'ssl' ) ) ) { + $api_url = set_url_scheme( $api_url, 'https' ); + } + + /** + * Default to a short TTL, to mitigate cache stampedes on high-traffic sites. + * This assumes that most errors will be short-lived, e.g., packet loss that causes the + * first request to fail, but a follow-up one will succeed. The value should be high + * enough to avoid stampedes, but low enough to not interfere with users manually + * re-trying a failed request. + */ + $cache_ttl = 5; + $wporg_response = wp_remote_get( $api_url ); + $raw_pattern_categories = json_decode( wp_remote_retrieve_body( $wporg_response ) ); + if ( is_wp_error( $wporg_response ) ) { + $raw_pattern_categories = $wporg_response; + + } elseif ( ! is_array( $raw_pattern_categories ) ) { + // HTTP request succeeded, but response data is invalid. + $raw_pattern_categories = new WP_Error( + 'pattern_directory_api_failed', + sprintf( + /* translators: %s: Support forums URL. */ + __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.' ), + __( 'https://wordpress.org/support/forums/' ) + ), + array( + 'response' => wp_remote_retrieve_body( $wporg_response ), + ) + ); + + } else { + // Response has valid data. + $cache_ttl = HOUR_IN_SECONDS; + } + + set_site_transient( $transient_key, $raw_pattern_categories, $cache_ttl ); + } + + if ( is_wp_error( $raw_pattern_categories ) ) { + $raw_pattern_categories->add_data( array( 'status' => 500 ) ); + + return $raw_pattern_categories; + } + + $response = array(); + + if ( $raw_pattern_categories ) { + foreach ( $raw_pattern_categories as $category ) { + $response[] = $this->prepare_response_for_collection( + $this->prepare_pattern_category_for_response( $category, $request ) + ); + } + } + + return new WP_REST_Response( $response ); + } + + /** + * Prepare a raw block pattern category before it gets output in a REST API response. + * + * @since 6.2.0 + * + * @param object $item Raw pattern category from api.wordpress.org, before any changes. + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response + */ + public function prepare_pattern_category_for_response( $item , $request) { + $prepared_pattern_category = array( + 'id' => absint( $item->id ), + 'name' => sanitize_text_field( $item->name ), + 'slug' => sanitize_text_field( $item->slug ) + ); + + $prepared_pattern = $this->add_additional_fields_to_object( $prepared_pattern_category, $request ); + + return new WP_REST_Response( $prepared_pattern_category ); + } + /** * Search and retrieve block patterns metadata * @@ -48,7 +167,7 @@ public function get_items( $request ) { $transient_key = $this->get_transient_key( $query_args ); - /* + /** * Use network-wide transient to improve performance. The locale is the only site * configuration that affects the response, and it's included in the transient key. */ @@ -60,7 +179,7 @@ public function get_items( $request ) { $api_url = set_url_scheme( $api_url, 'https' ); } - /* + /** * Default to a short TTL, to mitigate cache stampedes on high-traffic sites. * This assumes that most errors will be short-lived, e.g., packet loss that causes the * first request to fail, but a follow-up one will succeed. The value should be high diff --git a/phpunit/class-wp-rest-pattern-directory-controller-test.php b/phpunit/class-wp-rest-pattern-directory-controller-test.php index bf3bb8c4bf1888..fd34a915e96f61 100644 --- a/phpunit/class-wp-rest-pattern-directory-controller-test.php +++ b/phpunit/class-wp-rest-pattern-directory-controller-test.php @@ -53,7 +53,7 @@ public static function wpSetUpBeforeClass( $factory ) { self::$http_request_urls = array(); - static::$controller = new WP_REST_Pattern_Directory_Controller(); + static::$controller = new Gutenberg_REST_Pattern_Directory_Controller_6_2(); } public static function wpTearDownAfterClass() { @@ -68,6 +68,42 @@ public function tear_down() { parent::tear_down(); } + /** + * @covers WP_REST_Pattern_Directory_Controller::register_routes + * + * @since 5.8.0 + * @since 6.2.0 Added pattern directory categories endpoint. + */ + public function test_register_routes() { + $routes = rest_get_server()->get_routes(); + + $this->assertArrayHasKey( '/wp/v2/pattern-directory/patterns', $routes ); + $this->assertArrayHasKey( '/wp/v2/pattern-directory/categories', $routes ); + } + + /** + * @covers WP_REST_Pattern_Directory_Controller::prepare_pattern_category_for_response + * + * @since 6.2.0 + */ + public function test_prepare_pattern_category_for_response() { + $raw_categories = array( + (object) array( + 'id' => 3, + 'name' => 'Columns', + 'slug' => 'columns', + 'description' => 'A description', + ), + ); + + $prepared_category = static::$controller->prepare_response_for_collection( + static::$controller->prepare_pattern_category_for_response( $raw_categories[0], new WP_REST_Request() ) + ); + + $this->assertArrayNotHasKey( 'description', $prepared_category ); + } + + /** * Tests if the provided query args are passed through to the wp.org API. * @@ -168,13 +204,6 @@ function ( $preempt, $args, $url ) { ); } - /** - * @doesNotPerformAssertions - */ - public function test_register_routes() { - // Covered by the core test. - } - /** * @doesNotPerformAssertions */