Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Action_Endpoint
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 7
132
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 register_rest_routes
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 response_error
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 response_success
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 handle_action
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 create_nonce
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 permissions
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Register and handle custom action REST API Endpoints for data sync entries.
4 *
5 * @package automattic/jetpack-wp-js-data-sync
6 */
7
8namespace Automattic\Jetpack\WP_JS_Data_Sync\Endpoints;
9
10use Automattic\Jetpack\Schema\Schema_Parser;
11use Automattic\Jetpack\WP_JS_Data_Sync\Contracts\Data_Sync_Action;
12use Automattic\Jetpack\WP_JS_Data_Sync\DS_Utils;
13
14class Action_Endpoint {
15
16    /**
17     * @var Data_Sync_Action  - The class that handles the action.
18     */
19    private $action_class;
20
21    /**
22     * @var Schema_Parser $request_schema - The schema for requests to this action.
23     */
24    private $request_schema;
25
26    /**
27     * @var string $rest_namespace - The namespace for the REST API endpoint.
28     */
29    private $rest_namespace;
30
31    /**
32     * @var string $route - The route for the REST API endpoint.
33     */
34    private $route;
35
36    /**
37     * @var Authenticated_Nonce $nonce - The nonce for the REST API endpoint.
38     */
39    private $nonce;
40
41    /**
42     * This class handles endpoints for DataSync actions.
43     *
44     * @param string           $namespace
45     * @param string           $key
46     * @param string           $action_name
47     * @param Schema_Parser    $request_schema
48     * @param Data_Sync_Action $action_class
49     */
50    public function __construct( $namespace, $key, $action_name, $request_schema, $action_class ) {
51        $this->action_class   = $action_class;
52        $this->request_schema = $request_schema;
53        $this->rest_namespace = $namespace;
54        $this->route          = $key . '/action/' . $action_name;
55        $this->nonce          = new Authenticated_Nonce( "{$namespace}_{$this->route}_action" );
56    }
57
58    public function register_rest_routes() {
59        register_rest_route(
60            $this->rest_namespace,
61            $this->route,
62            array(
63                'methods'             => \WP_REST_Server::EDITABLE,
64                'callback'            => array( $this, 'handle_action' ),
65                'permission_callback' => array( $this, 'permissions' ),
66            )
67        );
68    }
69
70    private function response_error( $message, $code ) {
71        return rest_ensure_response(
72            array(
73                'status'  => 'error',
74                'message' => $message,
75                'code'    => $code,
76            )
77        );
78    }
79
80    private function response_success( $data ) {
81        return rest_ensure_response(
82            array(
83                'status' => 'success',
84                'JSON'   => $data,
85            )
86        );
87    }
88
89    public function handle_action( $request ) {
90        try {
91            $params      = $request->get_json_params();
92            $data        = $params['JSON'] ?? null;
93            $parsed_data = $this->request_schema->parse( $data );
94
95            // Delegate to the action handler
96            $result = $this->action_class->handle( $parsed_data, $request );
97            if ( is_wp_error( $result ) ) {
98                return $this->response_error( $result->get_error_message(), $result->get_error_code() );
99            }
100
101            if ( true === DS_Utils::debug_disable( $this->route ) ) {
102                // This is a debug request - it's ok to return a different shape from the rest.
103                // Return 418 I'm a teapot if this is a debug request to the endpoint.
104                return rest_ensure_response( new \WP_Error( 'teapot', "I'm a teapot.", array( 'status' => 418 ) ) );
105            }
106
107            return $this->response_success( $result );
108        } catch ( \RuntimeException $e ) {
109            return $this->response_error( $e->getMessage(), 'runtime_error' );
110        }
111    }
112
113    public function create_nonce() {
114        return $this->nonce->create();
115    }
116
117    /**
118     * @param \WP_REST_Request $request
119     */
120    public function permissions( $request ) {
121        $nonce = $request->get_header( 'X-Jetpack-WP-JS-Sync-Nonce' );
122        return $this->nonce->verify( $nonce ) && current_user_can( 'manage_options' );
123    }
124}