-
Notifications
You must be signed in to change notification settings - Fork 28
REST API: Add source
parameter for types endpoint
#128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
priethor
wants to merge
40
commits into
trunk
Choose a base branch
from
add/origin-parameter-for-types-endpoint
base: trunk
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
40 commits
Select commit
Hold shift + click to select a range
6453b6c
Initial commit, still with errors
cbravobernal dda11f7
Default should be false
cbravobernal ff4c6b0
Remove unit to try e2e
cbravobernal 5e704c1
Add query parameter to filter by post type registration ori
priethor 9f5576e
Fix PHPCS error
priethor 4c27770
Don't require the sidebar experiment to be enabled
priethor f61d429
First attempt at filtering post types by registration origin
priethor bad53df
Refactor to make it more elegant
priethor 1e128a3
Optimization
priethor 9b29cdb
Add origin to schema
priethor a4fcb14
Clean up response
priethor 4386792
Simplify and use a more elegant filter
priethor a3b901a
Fix single type queries
priethor bc62377
Cleanup
priethor 1b30485
Initial commit, still with errors
cbravobernal 4938c71
Default should be false
cbravobernal 08c005c
Remove unit to try e2e
cbravobernal 53f3531
Clean up response
priethor 1ccf496
Simplify and use a more elegant filter
priethor cebf109
Fix single type queries
priethor 978e331
Cleanup
priethor 2b76395
Resolved conflicts from rebase
priethor 7382fcf
Fix the scf fields experiment
priethor b75c136
Rename `origin` to `source`
priethor 2a2dfe1
Add unit and e2e tests
priethor 70133ce
Optimization!
priethor 2263520
Fix error in comments
priethor f5b72e5
Remove checks for SCF internal types, we only work with managed CPTs
priethor cbcc576
Update version references to specify SCF
priethor c0b5198
Undo changes done by mistake
priethor 3dc5034
Comment cleanup
priethor 602ffa4
Merge trunk into add/origin-parameter-for-types-endpoint
priethor bbd12f5
Fix `@since`annotation
priethor 49c2460
Remove console logging from the tests
priethor 28d571e
Fix syntax error
priethor a6671a7
Fix comment
priethor 17003e9
Make the e2e test more accurate yet simple
priethor 525b240
Merge branch 'trunk' into add/origin-parameter-for-types-endpoint
priethor 221cec4
Fix typo
priethor e5588b7
Fix the tests cleanup
priethor File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,19 +14,174 @@ | |
/** | ||
* Class SCF_Rest_Types_Endpoint | ||
* | ||
* Extends the /wp/v2/types endpoint to include SCF fields. | ||
* Extends the /wp/v2/types endpoint to include SCF fields and source filtering. | ||
* | ||
* @since SCF 6.5.0 | ||
*/ | ||
class SCF_Rest_Types_Endpoint { | ||
|
||
/** | ||
* Cached post types for the current request | ||
* | ||
* @var array|null | ||
*/ | ||
private $cached_post_types = null; | ||
|
||
/** | ||
* Initialize the class. | ||
* | ||
* @since SCF 6.5.0 | ||
*/ | ||
public function __construct() { | ||
add_action( 'rest_api_init', array( $this, 'register_extra_fields' ) ); | ||
add_action( 'rest_api_init', array( $this, 'register_parameters' ) ); | ||
|
||
// Add filter to process REST API requests by route | ||
add_filter( 'rest_request_before_callbacks', array( $this, 'filter_types_request' ), 10, 3 ); | ||
|
||
// Add filter to process each post type individually | ||
add_filter( 'rest_prepare_post_type', array( $this, 'filter_post_type' ), 10, 3 ); | ||
|
||
// Clean up null entries from the response | ||
add_filter( 'rest_pre_echo_response', array( $this, 'clean_types_response' ), 10, 3 ); | ||
} | ||
|
||
/** | ||
* Filter post types requests (both collection and individual) | ||
* | ||
* @since SCF 6.5.0 | ||
* | ||
* @param mixed $response The current response, either response or null. | ||
* @param array $handler The handler for the route. | ||
* @param WP_REST_Request $request The request object. | ||
* @return mixed The response or null. | ||
*/ | ||
public function filter_types_request( $response, $handler, $request ) { | ||
// Check if this is a types endpoint request | ||
$route = $request->get_route(); | ||
$is_collection = '/wp/v2/types' === $route; | ||
$is_single_type = preg_match( '#^/wp/v2/types/([^/]+)$#', $route, $matches ); | ||
|
||
if ( ! $is_collection && ! $is_single_type ) { | ||
return $response; | ||
} | ||
|
||
// Get the source parameter | ||
$source = $request->get_param( 'source' ); | ||
|
||
// Only proceed if source parameter is provided and valid | ||
if ( ! $source || ! in_array( $source, array( 'core', 'scf', 'other' ), true ) ) { | ||
return $response; | ||
} | ||
|
||
// Get post types, calculating once and reusing for the entire request | ||
if ( null === $this->cached_post_types ) { | ||
$this->cached_post_types = $this->get_source_post_types( $source ); | ||
} | ||
$source_post_types = $this->cached_post_types; | ||
|
||
// For single post type requests, check if it matches the source | ||
if ( $is_single_type && isset( $matches[1] ) ) { | ||
$requested_type = $matches[1]; | ||
|
||
// If the requested type doesn't match the source, return 404 | ||
if ( ! in_array( $requested_type, $source_post_types, true ) ) { | ||
return new WP_Error( | ||
'rest_post_type_invalid', | ||
__( 'Invalid post type.', 'secure-custom-fields' ), | ||
array( 'status' => 404 ) | ||
); | ||
} | ||
} | ||
// For collection requests, we don't need to add any filter here | ||
// as clean_types_response will handle removing null values from the response | ||
// and filter_post_type will handle individual filtering | ||
Comment on lines
+96
to
+98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In light of this, why even check for |
||
|
||
return $response; | ||
} | ||
|
||
/** | ||
* Filter individual post type in the response. | ||
* | ||
* @since SCF 6.5.0 | ||
* | ||
* @param WP_REST_Response $response The response object. | ||
* @param WP_Post_Type $post_type The post type object. | ||
* @param WP_REST_Request $request The request object. | ||
* @return WP_REST_Response|null The filtered response or null to filter it out. | ||
*/ | ||
public function filter_post_type( $response, $post_type, $request ) { | ||
$source = $request->get_param( 'source' ); | ||
|
||
// Only apply filtering if source parameter is provided and valid | ||
if ( ! $source || ! in_array( $source, array( 'core', 'scf', 'other' ), true ) ) { | ||
return $response; | ||
} | ||
|
||
if ( null === $this->cached_post_types ) { | ||
$this->cached_post_types = $this->get_source_post_types( $source ); | ||
} | ||
$source_post_types = $this->cached_post_types; | ||
|
||
if ( ! in_array( $post_type->name, $source_post_types, true ) ) { | ||
return null; | ||
} | ||
|
||
return $response; | ||
} | ||
|
||
/** | ||
* Get an array of post types for each source. | ||
* | ||
* @since SCF 6.5.0 | ||
* | ||
* @param string $source The source to get post types for. | ||
* @return array An array of post type names for the specified source. | ||
*/ | ||
private function get_source_post_types( $source ) { | ||
|
||
$core_types = array(); | ||
$scf_types = array(); | ||
|
||
if ( 'core' === $source || 'other' === $source ) { | ||
$all_post_types = get_post_types( array( '_builtin' => true ), 'objects' ); | ||
foreach ( $all_post_types as $post_type ) { | ||
$core_types[] = $post_type->name; | ||
} | ||
} | ||
|
||
if ( 'scf' === $source || 'other' === $source ) { | ||
$scf_types = array(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why the reassignment? |
||
|
||
// Get SCF-managed post types | ||
if ( function_exists( 'acf_get_internal_post_type_posts' ) ) { | ||
$scf_managed_post_types = acf_get_internal_post_type_posts( 'acf-post-type' ); | ||
foreach ( $scf_managed_post_types as $scf_post_type ) { | ||
if ( isset( $scf_post_type['post_type'] ) ) { | ||
$scf_types[] = $scf_post_type['post_type']; | ||
} | ||
} | ||
} | ||
} | ||
|
||
switch ( $source ) { | ||
case 'core': | ||
$result = $core_types; | ||
break; | ||
case 'scf': | ||
$result = $scf_types; | ||
break; | ||
case 'other': | ||
$result = array_diff( | ||
array_keys( get_post_types( array(), 'objects' ) ), | ||
array_merge( $core_types, $scf_types ) | ||
); | ||
break; | ||
default: | ||
$result = array(); | ||
} | ||
|
||
return $result; | ||
priethor marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/** | ||
|
@@ -40,6 +195,7 @@ public function register_extra_fields() { | |
if ( ! (bool) get_option( 'scf_beta_feature_editor_sidebar_enabled', false ) ) { | ||
return; | ||
} | ||
|
||
register_rest_field( | ||
'type', | ||
'scf_field_groups', | ||
|
@@ -123,4 +279,116 @@ private function get_field_schema() { | |
'context' => array( 'view', 'edit', 'embed' ), | ||
); | ||
} | ||
|
||
/** | ||
* Register the source parameter for the post types endpoint. | ||
* | ||
* @since SCF 6.5.0 | ||
*/ | ||
public function register_parameters() { | ||
if ( ! acf_get_setting( 'rest_api_enabled' ) ) { | ||
return; | ||
} | ||
|
||
// Register the query parameter with the REST API | ||
add_filter( 'rest_type_collection_params', array( $this, 'add_collection_params' ) ); | ||
add_filter( 'rest_types_collection_params', array( $this, 'add_collection_params' ) ); | ||
|
||
// Direct registration for OpenAPI documentation | ||
add_filter( 'rest_endpoints', array( $this, 'add_parameter_to_endpoints' ) ); | ||
} | ||
|
||
/** | ||
* Get the source parameter definition | ||
* | ||
* @since SCF 6.5.0 | ||
* | ||
* @param bool $include_validation Whether to include validation callbacks. | ||
* @return array Parameter definition | ||
*/ | ||
private function get_source_param_definition( $include_validation = false ) { | ||
$param = array( | ||
'description' => __( 'Filter post types by their source.', 'secure-custom-fields' ), | ||
'type' => 'string', | ||
'enum' => array( 'core', 'scf', 'other' ), | ||
'required' => false, | ||
); | ||
|
||
// Not needed for OpenAPI documentation | ||
if ( $include_validation ) { | ||
$param['validate_callback'] = 'rest_validate_request_arg'; | ||
$param['sanitize_callback'] = 'sanitize_text_field'; | ||
$param['default'] = null; | ||
$param['in'] = 'query'; | ||
} | ||
|
||
return $param; | ||
} | ||
|
||
/** | ||
* Add source parameter directly to the endpoints for proper documentation | ||
* | ||
* @since SCF 6.5.0 | ||
* | ||
* @param array $endpoints The REST API endpoints. | ||
* @return array Modified endpoints | ||
*/ | ||
public function add_parameter_to_endpoints( $endpoints ) { | ||
$source_param = $this->get_source_param_definition(); | ||
$endpoints_to_modify = array( '/wp/v2/types', '/wp/v2/types/(?P<type>[\w-]+)' ); | ||
|
||
foreach ( $endpoints_to_modify as $route ) { | ||
if ( isset( $endpoints[ $route ] ) ) { | ||
foreach ( $endpoints[ $route ] as &$endpoint ) { | ||
if ( isset( $endpoint['args'] ) ) { | ||
$endpoint['args']['source'] = $source_param; | ||
} | ||
} | ||
} | ||
} | ||
|
||
return $endpoints; | ||
} | ||
|
||
/** | ||
* Add source parameter to the collection parameters for the types endpoint. | ||
* | ||
* @since SCF 6.5.0 | ||
* | ||
* @param array $query_params JSON Schema-formatted collection parameters. | ||
* @return array Modified collection parameters. | ||
*/ | ||
public function add_collection_params( $query_params ) { | ||
$query_params['source'] = $this->get_source_param_definition( true ); | ||
return $query_params; | ||
} | ||
|
||
/** | ||
* Clean up null entries from the response | ||
* | ||
* @since SCF 6.5.0 | ||
* | ||
* @param array $response The response data. | ||
* @param WP_REST_Server $server The REST server instance. | ||
* @param WP_REST_Request $request The original request. | ||
* @return array The filtered response data. | ||
*/ | ||
public function clean_types_response( $response, $server, $request ) { | ||
if ( strpos( $request->get_route(), '/wp/v2/types' ) !== 0 ) { | ||
return $response; | ||
} | ||
|
||
// Only process collection responses (not single post type responses) | ||
// Single post type responses have a 'slug' property, collections don't | ||
if ( is_array( $response ) && ! isset( $response['slug'] ) ) { | ||
$response = array_filter( | ||
$response, | ||
function ( $entry ) { | ||
return null !== $entry; | ||
} | ||
); | ||
} | ||
|
||
return $response; | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In practice, the way the REST API works is that one request corresponds to one fresh execution of the WP stack, including running through
SCF_Rest_Types_Endpoint
's constructor and down that stack. However, semantically,cached_post_types
is an instance property of a class that represents an endpoint, not a request. Here's my concern when I read this chunk:Is there any scenario (e.g. request batching or sequential
rest_do_request
calls) where it's possible to process more than one request with the same endpoint instance? If so, this cache will need invalidation. At the very least, it should be source-indexed:… but ideally its lifetime should be that of a request.
No?