Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
65.82% covered (warning)
65.82%
52 / 79
25.00% covered (danger)
25.00%
2 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
WP_REST_WPCOM_Smart_Dictation_Client_Secret
65.82% covered (warning)
65.82%
52 / 79
25.00% covered (danger)
25.00%
2 / 8
35.97
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 register_rest_route
100.00% covered (success)
100.00%
34 / 34
100.00% covered (success)
100.00%
1 / 1
1
 create_client_secret
60.00% covered (warning)
60.00%
6 / 10
0.00% covered (danger)
0.00%
0 / 1
5.02
 settle_session
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_remaining_time
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 validate_session
83.33% covered (warning)
83.33%
10 / 12
0.00% covered (danger)
0.00%
0 / 1
7.23
 proxy_wpcom_request
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
20
 get_client_secret
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * WP_REST_WPCOM_Smart_Dictation_Client_Secret file.
4 *
5 * @package automattic/jetpack-mu-wpcom
6 */
7
8namespace A8C\WPCOM_DICTATION;
9
10use Automattic\Jetpack\Connection\Client;
11
12/**
13 * Class WP_REST_WPCOM_Smart_Dictation_Client_Secret.
14 *
15 * Proxies dictation client secret requests to the WPCOM platform endpoint.
16 */
17class WP_REST_WPCOM_Smart_Dictation_Client_Secret extends \WP_REST_Controller {
18
19    /**
20     * Session keys supported by the WPCOM endpoint.
21     */
22    private const SUPPORTED_SESSION_KEYS = array( 'instructions' );
23
24    /**
25     * WP_REST_WPCOM_Smart_Dictation_Client_Secret constructor.
26     */
27    public function __construct() {
28        $this->namespace = 'wpcom/v2';
29        $this->rest_base = '/dictation-client-secret';
30    }
31
32    /**
33     * Register available routes.
34     */
35    public function register_rest_route() {
36        register_rest_route(
37            $this->namespace,
38            $this->rest_base,
39            array(
40                'methods'             => \WP_REST_Server::CREATABLE,
41                'callback'            => array( $this, 'create_client_secret' ),
42                'permission_callback' => 'is_user_logged_in',
43                'args'                => array(
44                    'session' => array(
45                        'type'              => 'object',
46                        'required'          => false,
47                        'validate_callback' => array( $this, 'validate_session' ),
48                    ),
49                ),
50            )
51        );
52
53        register_rest_route(
54            $this->namespace,
55            $this->rest_base . '/settle',
56            array(
57                'methods'             => \WP_REST_Server::CREATABLE,
58                'callback'            => array( $this, 'settle_session' ),
59                'permission_callback' => 'is_user_logged_in',
60            )
61        );
62
63        register_rest_route(
64            $this->namespace,
65            $this->rest_base . '/remaining-time',
66            array(
67                'methods'             => \WP_REST_Server::READABLE,
68                'callback'            => array( $this, 'get_remaining_time' ),
69                'permission_callback' => 'is_user_logged_in',
70            )
71        );
72    }
73
74    /**
75     * Proxy the client secret request to the WPCOM platform endpoint.
76     *
77     * @param \WP_REST_Request $request The request sent to the API.
78     * @return \WP_REST_Response|\WP_Error
79     */
80    public function create_client_secret( \WP_REST_Request $request ) {
81        $session = $request->get_param( 'session' );
82        $payload = array();
83
84        if ( $request->has_param( 'session' ) ) {
85            $validation = $this->validate_session( $session );
86            if ( is_wp_error( $validation ) ) {
87                return $validation;
88            }
89
90            if ( is_object( $session ) ) {
91                $session = get_object_vars( $session );
92            }
93
94            $payload['session'] = $session;
95        }
96
97        return $this->proxy_wpcom_request( '/dictation-client-secret', 'POST', $payload );
98    }
99
100    /**
101     * Proxy the current session settlement request to the WPCOM platform endpoint.
102     *
103     * @return \WP_REST_Response|\WP_Error
104     */
105    public function settle_session() {
106        return $this->proxy_wpcom_request( '/dictation-client-secret/settle', 'POST' );
107    }
108
109    /**
110     * Proxy the remaining dictation time request to the WPCOM platform endpoint.
111     *
112     * @return \WP_REST_Response|\WP_Error
113     */
114    public function get_remaining_time() {
115        return $this->proxy_wpcom_request( '/dictation-client-secret/remaining-time' );
116    }
117
118    /**
119     * Validate optional session data.
120     *
121     * @param mixed $session Session data.
122     * @return true|\WP_Error
123     */
124    public function validate_session( $session ) {
125        if ( null === $session ) {
126            return true;
127        }
128
129        if ( is_object( $session ) ) {
130            $session = get_object_vars( $session );
131        }
132
133        if ( ! is_array( $session ) ) {
134            return new \WP_Error( 'rest_invalid_param', 'session must be an object.' );
135        }
136
137        $unsupported_keys = array_diff( array_keys( $session ), self::SUPPORTED_SESSION_KEYS );
138        if ( ! empty( $unsupported_keys ) ) {
139            return new \WP_Error( 'rest_invalid_param', 'session contains unsupported fields.' );
140        }
141
142        if ( isset( $session['instructions'] ) && ! is_string( $session['instructions'] ) ) {
143            return new \WP_Error( 'rest_invalid_param', 'session.instructions must be a string.' );
144        }
145
146        return true;
147    }
148
149    /**
150     * Proxy a request to the WPCOM platform endpoint.
151     *
152     * @param string     $path   The WPCOM endpoint path.
153     * @param string     $method The request method.
154     * @param array|null $data   Optional request body data.
155     * @return \WP_REST_Response|\WP_Error
156     */
157    private function proxy_wpcom_request( $path, $method = 'GET', $data = null ) {
158        $request_args = array( 'method' => $method );
159
160        if ( null === $data ) {
161            $body = Client::wpcom_json_api_request_as_user(
162                $path,
163                '2',
164                $request_args
165            );
166        } else {
167            $body = Client::wpcom_json_api_request_as_user(
168                $path,
169                '2',
170                $request_args,
171                $data
172            );
173        }
174
175        if ( is_wp_error( $body ) ) {
176            return $body;
177        }
178
179        $response = json_decode( wp_remote_retrieve_body( $body ) );
180        $status   = wp_remote_retrieve_response_code( $body );
181
182        return new \WP_REST_Response( $response, $status ? $status : 200 );
183    }
184
185    /**
186     * Backwards-compatible alias for the original callback name.
187     *
188     * @param \WP_REST_Request $request The request sent to the API.
189     * @return \WP_REST_Response|\WP_Error
190     */
191    public function get_client_secret( \WP_REST_Request $request ) {
192        return $this->create_client_secret( $request );
193    }
194}