Changeset 59032
- Timestamp:
- 09/17/2024 09:50:38 PM (3 months ago)
- Location:
- trunk
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/rest-api/class-wp-rest-server.php
r58688 r59032 637 637 $attributes = $item['attributes']; 638 638 $attributes['href'] = $item['href']; 639 $data[ $rel ][] = $attributes; 639 640 if ( 'self' !== $rel ) { 641 $data[ $rel ][] = $attributes; 642 continue; 643 } 644 645 $target_hints = self::get_target_hints_for_link( $attributes ); 646 if ( $target_hints ) { 647 $attributes['targetHints'] = $target_hints; 648 } 649 650 $data[ $rel ][] = $attributes; 640 651 } 641 652 } 642 653 643 654 return $data; 655 } 656 657 /** 658 * Gets the target links for a REST API Link. 659 * 660 * @since 6.7.0 661 * 662 * @param array $link 663 * 664 * @return array|null 665 */ 666 protected static function get_target_hints_for_link( $link ) { 667 // Prefer targetHints that were specifically designated by the developer. 668 if ( isset( $link['targetHints']['allow'] ) ) { 669 return null; 670 } 671 672 $request = WP_REST_Request::from_url( $link['href'] ); 673 if ( ! $request ) { 674 return null; 675 } 676 677 $server = rest_get_server(); 678 $match = $server->match_request_to_handler( $request ); 679 680 if ( is_wp_error( $match ) ) { 681 return null; 682 } 683 684 if ( is_wp_error( $request->has_valid_params() ) ) { 685 return null; 686 } 687 688 if ( is_wp_error( $request->sanitize_params() ) ) { 689 return null; 690 } 691 692 $target_hints = array(); 693 694 $response = new WP_REST_Response(); 695 $response->set_matched_route( $match[0] ); 696 $response->set_matched_handler( $match[1] ); 697 $headers = rest_send_allow_header( $response, $server, $request )->get_headers(); 698 699 foreach ( $headers as $name => $value ) { 700 $name = WP_REST_Request::canonicalize_header_name( $name ); 701 702 $target_hints[ $name ] = array_map( 'trim', explode( ',', $value ) ); 703 } 704 705 return $target_hints; 644 706 } 645 707 -
trunk/tests/phpunit/tests/rest-api/rest-server.php
r57987 r59032 10 10 class Tests_REST_Server extends WP_Test_REST_TestCase { 11 11 protected static $icon_id; 12 protected static $admin_id; 13 protected static $post_id; 12 14 13 15 /** … … 22 24 23 25 public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { 24 $filename = DIR_TESTDATA . '/images/test-image-large.jpg'; 25 self::$icon_id = $factory->attachment->create_upload_object( $filename ); 26 $filename = DIR_TESTDATA . '/images/test-image-large.jpg'; 27 self::$icon_id = $factory->attachment->create_upload_object( $filename ); 28 self::$admin_id = $factory->user->create( 29 array( 30 'role' => 'administrator', 31 ) 32 ); 33 self::$post_id = $factory->post->create(); 26 34 } 27 35 28 36 public static function tear_down_after_class() { 29 37 wp_delete_attachment( self::$icon_id, true ); 38 self::delete_user( self::$admin_id ); 39 wp_delete_post( self::$post_id ); 30 40 31 41 parent::tear_down_after_class(); … … 2432 2442 } 2433 2443 2444 /** 2445 * @ticket 61739 2446 */ 2447 public function test_validates_request_when_building_target_hints() { 2448 register_rest_route( 2449 'test-ns/v1', 2450 '/test/(?P<id>\d+)', 2451 array( 2452 array( 2453 'methods' => \WP_REST_Server::READABLE, 2454 'callback' => static function () { 2455 return new \WP_REST_Response(); 2456 }, 2457 'permission_callback' => '__return_true', 2458 'args' => array( 2459 'id' => array( 2460 'type' => 'integer', 2461 ), 2462 ), 2463 ), 2464 ) 2465 ); 2466 2467 $response = new WP_REST_Response(); 2468 $response->add_link( 'self', rest_url( 'test-ns/v1/test/garbage' ) ); 2469 2470 $links = rest_get_server()::get_response_links( $response ); 2471 2472 $this->assertArrayHasKey( 'self', $links ); 2473 $this->assertArrayNotHasKey( 'targetHints', $links['self'][0] ); 2474 } 2475 2476 /** 2477 * @ticket 61739 2478 */ 2479 public function test_sanitizes_request_when_building_target_hints() { 2480 $validated_param = null; 2481 register_rest_route( 2482 'test-ns/v1', 2483 '/test/(?P<id>\d+)', 2484 array( 2485 array( 2486 'methods' => \WP_REST_Server::READABLE, 2487 'callback' => static function () { 2488 return new \WP_REST_Response(); 2489 }, 2490 'permission_callback' => function ( WP_REST_Request $request ) use ( &$validated_param ) { 2491 $validated_param = $request['id']; 2492 2493 return true; 2494 }, 2495 'args' => array( 2496 'id' => array( 2497 'type' => 'integer', 2498 ), 2499 ), 2500 ), 2501 ) 2502 ); 2503 2504 $response = new WP_REST_Response(); 2505 $response->add_link( 'self', rest_url( 'test-ns/v1/test/5' ) ); 2506 2507 $links = rest_get_server()::get_response_links( $response ); 2508 2509 $this->assertArrayHasKey( 'self', $links ); 2510 $this->assertArrayHasKey( 'targetHints', $links['self'][0] ); 2511 $this->assertIsInt( $validated_param ); 2512 } 2513 2514 /** 2515 * @ticket 61739 2516 */ 2517 public function test_populates_target_hints_for_administrator() { 2518 wp_set_current_user( self::$admin_id ); 2519 $response = rest_do_request( '/wp/v2/posts' ); 2520 $post = $response->get_data()[0]; 2521 2522 $link = $post['_links']['self'][0]; 2523 $this->assertArrayHasKey( 'targetHints', $link ); 2524 $this->assertArrayHasKey( 'allow', $link['targetHints'] ); 2525 $this->assertSame( array( 'GET', 'POST', 'PUT', 'PATCH', 'DELETE' ), $link['targetHints']['allow'] ); 2526 } 2527 2528 /** 2529 * @ticket 61739 2530 */ 2531 public function test_populates_target_hints_for_logged_out_user() { 2532 $response = rest_do_request( '/wp/v2/posts' ); 2533 $post = $response->get_data()[0]; 2534 2535 $link = $post['_links']['self'][0]; 2536 $this->assertArrayHasKey( 'targetHints', $link ); 2537 $this->assertArrayHasKey( 'allow', $link['targetHints'] ); 2538 $this->assertSame( array( 'GET' ), $link['targetHints']['allow'] ); 2539 } 2540 2541 /** 2542 * @ticket 61739 2543 */ 2544 public function test_does_not_error_on_invalid_urls() { 2545 $response = new WP_REST_Response(); 2546 $response->add_link( 'self', 'this is not a real URL' ); 2547 2548 $links = rest_get_server()::get_response_links( $response ); 2549 $this->assertArrayNotHasKey( 'targetHints', $links['self'][0] ); 2550 } 2551 2552 /** 2553 * @ticket 61739 2554 */ 2555 public function test_does_not_error_on_bad_rest_api_routes() { 2556 $response = new WP_REST_Response(); 2557 $response->add_link( 'self', rest_url( '/this/is/not/a/real/route' ) ); 2558 2559 $links = rest_get_server()::get_response_links( $response ); 2560 $this->assertArrayNotHasKey( 'targetHints', $links['self'][0] ); 2561 } 2562 2563 /** 2564 * @ticket 61739 2565 */ 2566 public function test_prefers_developer_defined_target_hints() { 2567 $response = new WP_REST_Response(); 2568 $response->add_link( 2569 'self', 2570 '/wp/v2/posts/' . self::$post_id, 2571 array( 2572 'targetHints' => array( 2573 'allow' => array( 'GET', 'PUT' ), 2574 ), 2575 ) 2576 ); 2577 2578 $links = rest_get_server()::get_response_links( $response ); 2579 $link = $links['self'][0]; 2580 $this->assertArrayHasKey( 'targetHints', $link ); 2581 $this->assertArrayHasKey( 'allow', $link['targetHints'] ); 2582 $this->assertSame( array( 'GET', 'PUT' ), $link['targetHints']['allow'] ); 2583 } 2584 2434 2585 public function _validate_as_integer_123( $value, $request, $key ) { 2435 2586 if ( ! is_int( $value ) ) { -
trunk/tests/qunit/fixtures/wp-api-generated.js
r58822 r59032 12335 12335 "self": [ 12336 12336 { 12337 "href": "http://example.org/index.php?rest_route=/wp/v2/posts/4" 12337 "href": "http://example.org/index.php?rest_route=/wp/v2/posts/4", 12338 "targetHints": { 12339 "allow": [ 12340 "GET", 12341 "POST", 12342 "PUT", 12343 "PATCH", 12344 "DELETE" 12345 ] 12346 } 12338 12347 } 12339 12348 ], … … 12642 12651 "self": [ 12643 12652 { 12644 "href": "http://example.org/index.php?rest_route=/wp/v2/pages/7" 12653 "href": "http://example.org/index.php?rest_route=/wp/v2/pages/7", 12654 "targetHints": { 12655 "allow": [ 12656 "GET", 12657 "POST", 12658 "PUT", 12659 "PATCH", 12660 "DELETE" 12661 ] 12662 } 12645 12663 } 12646 12664 ], … … 12933 12951 "self": [ 12934 12952 { 12935 "href": "http://example.org/index.php?rest_route=/wp/v2/media/10" 12953 "href": "http://example.org/index.php?rest_route=/wp/v2/media/10", 12954 "targetHints": { 12955 "allow": [ 12956 "GET", 12957 "POST", 12958 "PUT", 12959 "PATCH", 12960 "DELETE" 12961 ] 12962 } 12936 12963 } 12937 12964 ], … … 13630 13657 "self": [ 13631 13658 { 13632 "href": "http://example.org/index.php?rest_route=/wp/v2/categories/1" 13659 "href": "http://example.org/index.php?rest_route=/wp/v2/categories/1", 13660 "targetHints": { 13661 "allow": [ 13662 "GET", 13663 "POST", 13664 "PUT", 13665 "PATCH" 13666 ] 13667 } 13633 13668 } 13634 13669 ], … … 13695 13730 "self": [ 13696 13731 { 13697 "href": "http://example.org/index.php?rest_route=/wp/v2/tags/2" 13732 "href": "http://example.org/index.php?rest_route=/wp/v2/tags/2", 13733 "targetHints": { 13734 "allow": [ 13735 "GET", 13736 "POST", 13737 "PUT", 13738 "PATCH", 13739 "DELETE" 13740 ] 13741 } 13698 13742 } 13699 13743 ], … … 13759 13803 "self": [ 13760 13804 { 13761 "href": "http://example.org/index.php?rest_route=/wp/v2/users/1" 13805 "href": "http://example.org/index.php?rest_route=/wp/v2/users/1", 13806 "targetHints": { 13807 "allow": [ 13808 "GET", 13809 "POST", 13810 "PUT", 13811 "PATCH", 13812 "DELETE" 13813 ] 13814 } 13762 13815 } 13763 13816 ], … … 13787 13840 "self": [ 13788 13841 { 13789 "href": "http://example.org/index.php?rest_route=/wp/v2/users/2" 13842 "href": "http://example.org/index.php?rest_route=/wp/v2/users/2", 13843 "targetHints": { 13844 "allow": [ 13845 "GET", 13846 "POST", 13847 "PUT", 13848 "PATCH", 13849 "DELETE" 13850 ] 13851 } 13790 13852 } 13791 13853 ], … … 13860 13922 "self": [ 13861 13923 { 13862 "href": "http://example.org/index.php?rest_route=/wp/v2/comments/2" 13924 "href": "http://example.org/index.php?rest_route=/wp/v2/comments/2", 13925 "targetHints": { 13926 "allow": [ 13927 "GET", 13928 "POST", 13929 "PUT", 13930 "PATCH", 13931 "DELETE" 13932 ] 13933 } 13863 13934 } 13864 13935 ],
Note: See TracChangeset
for help on using the changeset viewer.