Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
50.00% covered (danger)
50.00%
28 / 56
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
REST_Zendesk_Chat
50.00% covered (danger)
50.00%
28 / 56
25.00% covered (danger)
25.00%
1 / 4
30.00
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
1 / 1
1
 chat_authentication_permissions_callback
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 get_chat_authentication
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
42
 get_chat_availability
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * Sets up the Zendesk Chat REST API endpoints.
4 *
5 * @package automattic/my-jetpack
6 */
7
8namespace Automattic\Jetpack\My_Jetpack;
9
10use Automattic\Jetpack\Connection\Client;
11use WP_Error;
12use WP_REST_Response;
13
14/**
15 * Registers the REST routes for Zendesk Chat.
16 *
17 * @phan-constructor-used-for-side-effects
18 */
19class REST_Zendesk_Chat {
20    const TRANSIENT_EXPIRY   = 1 * MINUTE_IN_SECONDS * 60 * 24 * 7; // 1 week (JWT is actually 2 weeks, but lets be on the safe side)
21    const ZENDESK_AUTH_TOKEN = 'zendesk_auth_token';
22    /**
23     * Constructor.
24     */
25    public function __construct() {
26        register_rest_route(
27            'my-jetpack/v1',
28            'chat/availability',
29            array(
30                'methods'             => \WP_REST_Server::READABLE,
31                'callback'            => __CLASS__ . '::get_chat_availability',
32                'permission_callback' => __CLASS__ . '::chat_authentication_permissions_callback',
33            )
34        );
35
36        register_rest_route(
37            'my-jetpack/v1',
38            'chat/authentication',
39            array(
40                'methods'             => \WP_REST_Server::READABLE,
41                'callback'            => __CLASS__ . '::get_chat_authentication',
42                'args'                => array(
43                    'type'      => array(
44                        'required' => false,
45                        'type'     => 'string',
46                    ),
47                    'test_mode' => array(
48                        'required' => false,
49                        'type'     => 'boolean',
50                    ),
51                ),
52                'permission_callback' => __CLASS__ . '::chat_authentication_permissions_callback',
53            )
54        );
55    }
56
57    /**
58     * Ensure user is logged in if making an authentication request
59     *
60     * @access public
61     * @static
62     *
63     * @return WP_Error|true
64     */
65    public static function chat_authentication_permissions_callback() {
66        if ( ! get_current_user_id() ) {
67            return new WP_Error( 'unauthorized', 'You must be logged in to access this resource.', array( 'status' => 401 ) );
68        }
69
70        return true;
71    }
72
73    /**
74     * Gets the chat authentication token.
75     *
76     * @return WP_Error|WP_REST_Response { token: string }
77     */
78    public static function get_chat_authentication() {
79        $authentication = get_transient( self::ZENDESK_AUTH_TOKEN );
80        if ( $authentication ) {
81            return rest_ensure_response( $authentication );
82        }
83
84        $proxied           = function_exists( 'wpcom_is_proxied_request' ) ? wpcom_is_proxied_request() : false;
85        $wpcom_endpoint    = 'help/authenticate/chat';
86        $wpcom_api_version = '2';
87
88        $body = array(
89            'type'      => 'zendesk',
90            'test_mode' => $proxied ? true : false,
91        );
92
93        $response      = Client::wpcom_json_api_request_as_user( $wpcom_endpoint, $wpcom_api_version, array( 'method' => 'POST' ), $body );
94        $response_code = wp_remote_retrieve_response_code( $response );
95        $body          = json_decode( wp_remote_retrieve_body( $response ) );
96
97        if ( is_wp_error( $response ) || empty( $response['body'] ) ) {
98            return new WP_Error( 'chat_authentication_failed', 'Chat authentication failed', array( 'status' => $response_code ) );
99        }
100
101        set_transient( self::ZENDESK_AUTH_TOKEN, $body, self::TRANSIENT_EXPIRY );
102        return rest_ensure_response( $body );
103    }
104
105    /**
106     * Calls `wpcom/v2/presales/chat?group=jp_presales` endpoint.
107     * This endpoint returns whether or not the Jetpack presales chat group is available
108     *
109     * @return WP_Error|WP_REST_Response { is_available: bool }
110     */
111    public static function get_chat_availability() {
112        $wpcom_endpoint    = '/presales/chat?group=jp_presales';
113        $wpcom_api_version = '2';
114        $response          = Client::wpcom_json_api_request_as_user( $wpcom_endpoint, $wpcom_api_version );
115        $response_code     = wp_remote_retrieve_response_code( $response );
116        $body              = json_decode( wp_remote_retrieve_body( $response ) );
117
118        if ( is_wp_error( $response ) || empty( $response['body'] ) ) {
119            return new WP_Error( 'chat_config_data_fetch_failed', 'Chat config data fetch failed', array( 'status' => $response_code ) );
120        }
121
122        return rest_ensure_response( $body );
123    }
124}