Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
File Manager
/
wp-content
/
plugins
/
woocommerce
/
src
/
Internal
/
PushNotifications
/
Controllers
:
StepLogRestController.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php declare( strict_types = 1 ); namespace Automattic\WooCommerce\Internal\PushNotifications\Controllers; defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Internal\PushNotifications\Notifications\Notification; use Automattic\WooCommerce\Internal\PushNotifications\Services\StepLogQuery; use Automattic\WooCommerce\Internal\PushNotifications\Traits\AuthorizesPushNotificationRequests; use Automattic\WooCommerce\Internal\RestApiControllerBase; use WP_REST_Request; use WP_REST_Response; use WP_REST_Server; /** * Read-only REST endpoints through which Mission Control reads the push * notification step log: everything recorded on the store, the journey of one * notification, and the attempts made against one device or one user's devices. * * Only WPCOM can call these. Every response carries the store's logging state * and the earliest time the read covered, so a screen with no rows can say why. * * @since 11.3.0 */ class StepLogRestController extends RestApiControllerBase { use AuthorizesPushNotificationRequests; /** * How far back a request reads when it gives no `from`. */ const DEFAULT_RANGE_SECONDS = 2 * DAY_IN_SECONDS; /** * The longest range a request may ask for. * * The file reader opens one path per day per rotation per source, so an * unbounded range is a stat call per day of however long the caller asked * for. Set past the 30 day default log retention, since nothing older than * that survives to be read anyway. */ const MAX_RANGE_SECONDS = 90 * DAY_IN_SECONDS; /** * The root namespace for the JSON REST API endpoints. * * @var string */ protected string $route_namespace = 'wc-push-notifications'; /** * The REST base for the endpoints URL. * * @var string */ protected string $rest_base = 'step-logs'; /** * Rows returned when the caller asks for no particular number. */ const DEFAULT_LIMIT = 100; /** * Most rows a caller may ask for in one page. */ const MAX_LIMIT = 500; /** * The step log query service. * * @var StepLogQuery */ private StepLogQuery $query; /** * Initialize injected dependencies. * * @internal * * @param StepLogQuery $query The step log query service. * * @since 11.3.0 */ final public function init( StepLogQuery $query ): void { $this->query = $query; } /** * Class identifier used by `woocommerce_rest_api_get_rest_namespaces`. * * Distinct from the URL namespace, because the filter keys one class per * value and sibling controllers share the URL namespace. * * @return string * * @since 11.3.0 */ protected function get_rest_api_namespace(): string { return 'wc-push-notifications-step-logs'; } /** * Registers the routes. * * @return void * * @since 11.3.0 */ public function register_routes(): void { register_rest_route( $this->route_namespace, $this->rest_base . '/site', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => fn ( WP_REST_Request $request ) => $this->run( $request, 'get_site' ), 'permission_callback' => array( $this, 'authorize_as_from_wpcom' ), 'args' => $this->get_range_args(), ), ) ); register_rest_route( $this->route_namespace, $this->rest_base . '/notifications/(?P<type>[a-z_]+)/(?P<resource_id>\d+)', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => fn ( WP_REST_Request $request ) => $this->run( $request, 'get_notification' ), 'permission_callback' => array( $this, 'authorize_as_from_wpcom' ), 'args' => array_merge( array( 'type' => array( 'type' => 'string', 'required' => true, 'enum' => array_keys( Notification::NOTIFICATION_CLASSES ), 'sanitize_callback' => 'sanitize_key', 'validate_callback' => 'rest_validate_request_arg', ), 'resource_id' => array( 'type' => 'integer', 'required' => true, 'minimum' => 1, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ), ), $this->get_range_args() ), ), ) ); register_rest_route( $this->route_namespace, $this->rest_base . '/tokens/(?P<token_id>\d+)', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => fn ( WP_REST_Request $request ) => $this->run( $request, 'get_token' ), 'permission_callback' => array( $this, 'authorize_as_from_wpcom' ), 'args' => array_merge( array( 'token_id' => array( 'type' => 'integer', 'required' => true, 'minimum' => 1, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ), ), $this->get_range_args() ), ), ) ); register_rest_route( $this->route_namespace, $this->rest_base . '/users/(?P<user_id>\d+)', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => fn ( WP_REST_Request $request ) => $this->run( $request, 'get_user' ), 'permission_callback' => array( $this, 'authorize_as_from_wpcom' ), 'args' => array_merge( array( 'user_id' => array( 'type' => 'integer', 'required' => true, 'minimum' => 1, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ), ), $this->get_range_args() ), ), ) ); } /** * Handles the notification journey request. * * @param WP_REST_Request $request The request object. * @phpstan-param WP_REST_Request<array<string, mixed>> $request * @return WP_REST_Response * * @since 11.3.0 */ protected function get_notification( WP_REST_Request $request ): WP_REST_Response { list( $from, $to ) = $this->get_range( $request ); return new WP_REST_Response( $this->query->for_notification( (string) $request->get_param( 'type' ), (int) $request->get_param( 'resource_id' ), $from, $to, $this->get_limit( $request ) ) ); } /** * Handles the whole-store request. * * @param WP_REST_Request $request The request object. * @phpstan-param WP_REST_Request<array<string, mixed>> $request * @return WP_REST_Response * * @since 11.3.0 */ protected function get_site( WP_REST_Request $request ): WP_REST_Response { list( $from, $to ) = $this->get_range( $request ); return new WP_REST_Response( $this->query->for_site( $from, $to, $this->get_limit( $request ) ) ); } /** * Handles the device request. * * @param WP_REST_Request $request The request object. * @phpstan-param WP_REST_Request<array<string, mixed>> $request * @return WP_REST_Response * * @since 11.3.0 */ protected function get_token( WP_REST_Request $request ): WP_REST_Response { list( $from, $to ) = $this->get_range( $request ); return new WP_REST_Response( $this->query->for_token( (int) $request->get_param( 'token_id' ), $from, $to, $this->get_limit( $request ) ) ); } /** * Handles the user request. * * @param WP_REST_Request $request The request object. * @phpstan-param WP_REST_Request<array<string, mixed>> $request * @return WP_REST_Response * * @since 11.3.0 */ protected function get_user( WP_REST_Request $request ): WP_REST_Response { list( $from, $to ) = $this->get_range( $request ); return new WP_REST_Response( $this->query->for_user( (int) $request->get_param( 'user_id' ), $from, $to, $this->get_limit( $request ) ) ); } /** * The `from` and `to` arguments every route accepts, as Unix timestamps. * * @return array */ private function get_range_args(): array { return array( 'limit' => array( 'description' => __( 'Rows to return, newest first. A page carries every row of its last second, so it can exceed this.', 'woocommerce' ), 'type' => 'integer', 'default' => self::DEFAULT_LIMIT, 'minimum' => 1, 'maximum' => self::MAX_LIMIT, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ), 'from' => array( 'description' => __( 'Earliest Unix timestamp to include. Defaults to 48 hours before `to`, and is clamped to 90 days before it.', 'woocommerce' ), 'type' => 'integer', 'minimum' => 0, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ), 'to' => array( 'description' => __( 'Latest Unix timestamp to include. Defaults to now.', 'woocommerce' ), 'type' => 'integer', 'minimum' => 0, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ), ); } /** * The page size the request asked for, or the default. * * @param WP_REST_Request $request The request object. * @phpstan-param WP_REST_Request<array<string, mixed>> $request * @return int */ private function get_limit( WP_REST_Request $request ): int { $limit = (int) $request->get_param( 'limit' ); return $limit > 0 ? $limit : self::DEFAULT_LIMIT; } /** * Resolves the requested time range, applying the defaults. * * @param WP_REST_Request $request The request object. * @phpstan-param WP_REST_Request<array<string, mixed>> $request * @return array{0: int, 1: int} */ private function get_range( WP_REST_Request $request ): array { $to = (int) $request->get_param( 'to' ); $from = (int) $request->get_param( 'from' ); if ( $to <= 0 ) { $to = time(); } if ( $from <= 0 ) { $from = $to - self::DEFAULT_RANGE_SECONDS; } $from = min( $from, $to ); return array( max( $from, $to - self::MAX_RANGE_SECONDS ), $to ); } }