Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
6.90% covered (danger)
6.90%
8 / 116
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Events
6.90% covered (danger)
6.90%
8 / 116
0.00% covered (danger)
0.00%
0 / 5
149.39
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 register_routes
0.00% covered (danger)
0.00%
0 / 52
0.00% covered (danger)
0.00%
0 / 1
2
 create_item
42.11% covered (danger)
42.11%
8 / 19
0.00% covered (danger)
0.00%
0 / 1
7.10
 create_item_permissions_check
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 create_items
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3namespace NewfoldLabs\WP\Module\Data\API;
4
5use NewfoldLabs\WP\Module\Data\Event;
6use NewfoldLabs\WP\Module\Data\EventManager;
7use NewfoldLabs\WP\Module\Data\HiiveConnection;
8use WP_REST_Controller;
9use WP_REST_Server;
10
11/**
12 * REST API controller for sending events to the hiive.
13 */
14class Events extends WP_REST_Controller {
15
16    /**
17     * Instance of the EventManager class.
18     *
19     * @var EventManager
20     */
21    public $event_manager;
22
23    /**
24     * Instance of the HiiveConnection class.
25     *
26     * @var HiiveConnection
27     */
28    public $hiive;
29
30    /**
31     * Events constructor.
32     *
33     * @param HiiveConnection $hiive           Instance of the HiiveConnection class.
34     * @param EventManager    $event_manager Instance of the EventManager class.
35     */
36    public function __construct( HiiveConnection $hiive, EventManager $event_manager ) {
37        $this->event_manager = $event_manager;
38        $this->hiive         = $hiive;
39        $this->namespace     = 'newfold-data/v1';
40        $this->rest_base     = 'events';
41    }
42
43    /**
44     * Registers the routes for the objects of the controller.
45     *
46     * @see register_rest_route()
47     * @see EventManager::rest_api_init()
48     */
49    public function register_routes() {
50
51        register_rest_route(
52            $this->namespace,
53            '/' . $this->rest_base . '/',
54            array(
55                'args' => array(
56                    'action'   => array(
57                        'required'          => true,
58                        'description'       => __( 'Event action. For the "pageview" action/key, Hiive tries to read the page URL and page title only from the "page" and "page_title" keys in the data arg.' ),
59                        'type'              => 'string',
60                        'sanitize_callback' => function ( $value ) {
61                            return sanitize_title( $value );
62                        },
63                    ),
64                    'category' => array(
65                        'default'           => 'admin',
66                        'description'       => __( 'Event category' ),
67                        'type'              => 'string',
68                        'sanitize_callback' => function ( $value ) {
69                            return sanitize_title( $value );
70                        },
71                    ),
72                    'data'     => array(
73                        'description' => __( 'Event data' ),
74                        'type'        => 'object',
75                    ),
76                    'queue'    => array(
77                        'default'           => true,
78                        'description'       => __( 'Whether or not to queue the event' ),
79                        'type'              => 'boolean',
80                        'sanitize_callback' => function ( $value ) {
81                            return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
82                        },
83                    ),
84                ),
85                array(
86                    'methods'             => WP_REST_Server::CREATABLE,
87                    'callback'            => array( $this, 'create_item' ),
88                    'permission_callback' => array( $this, 'create_item_permissions_check' ),
89                ),
90            )
91        );
92
93        \register_rest_route(
94            $this->namespace,
95            '/' . $this->rest_base . '/batch',
96            array(
97                array(
98                    'methods'             => \WP_REST_Server::CREATABLE,
99                    'callback'            => array( $this, 'create_items' ),
100                    'permission_callback' => array( $this, 'create_item_permissions_check' ),
101                ),
102            )
103        );
104    }
105
106    /**
107     * Dispatches a new event.
108     *
109     * `wp-json/newfold-data/v1/events`
110     *
111     * @param \WP_REST_Request $request Full details about the request.
112     *
113     * @used-by newfold-notifications/v1/notifications/
114     * @used-by NotificationsApi::registerRoutes() (in callback)
115     * @used-by wp-module-notifications/assets/js/realtime-notices.js:189
116     *
117     * @return \WP_REST_Response|\WP_Error Response object on success, or WP_Error object on failure.
118     */
119    public function create_item( $request ) {
120
121        $category = $request->get_param( 'category' );
122        $action   = $request->get_param( 'action' );
123        $data     = ! empty( $request['data'] ) ? $request['data'] : array();
124
125        $event = new Event( $category, $action, $data );
126
127        // If request isn't to be queued, we want the realtime response.
128        if ( ! $request['queue'] ) {
129            $hiive_response_notifications = $this->hiive->send_event( $event );
130
131            if ( is_wp_error( $hiive_response_notifications ) ) {
132                return new \WP_REST_Response( $hiive_response_notifications->get_error_message(), 500 );
133            }
134
135            return new \WP_REST_Response( array( 'data' => $hiive_response_notifications ), 201 );
136        }
137
138        // Otherwise, queue the event.
139        $this->event_manager->push( $event );
140
141        $response = rest_ensure_response(
142            array(
143                'category' => $category,
144                'action'   => $action,
145                'data'     => $data,
146            )
147        );
148        // 202 – "The request has been accepted for processing, but the processing has not been completed.".
149        $response->set_status( 202 );
150
151        return $response;
152    }
153
154    /**
155     * User is required to be logged in.
156     *
157     * @param \WP_REST_Request $request Full details about the request.
158     *
159     * @return true|\WP_Error
160     *
161     * @since 1.0
162     */
163    public function create_item_permissions_check( $request ) {
164        if ( ! current_user_can( 'read' ) ) {
165            return new \WP_Error(
166                'rest_cannot_log_event',
167                __( 'Sorry, you are not allowed to use this endpoint.' ),
168                array( 'status' => rest_authorization_required_code() )
169            );
170        }
171
172        return true;
173    }
174
175    /**
176     * Manages sending a batch of events to the single event API.
177     *
178     * @param \WP_REST_Request $request A request containing an array of events.
179     * @return \WP_REST_Response|\WP_Error Response object on success, or WP_Error object on failure.
180     */
181    public function create_items( $request ) {
182        $events = $request->get_json_params();
183        if ( ! rest_is_array( $events ) ) {
184            return new \WP_Error(
185                'rest_cannot_log_events',
186                __( 'Request does not contain an array of events.' )
187            );
188        }
189
190        $errors = array();
191        foreach ( $events as $index => $event ) {
192            $event_request = new \WP_REST_Request(
193                \WP_REST_Server::CREATABLE,
194                "/{$this->namespace}/{$this->rest_base}"
195            );
196            $event_request->set_body_params( $event );
197            $response = \rest_do_request( $event_request );
198            if ( $response->is_error() ) {
199                array_push(
200                    $errors,
201                    array(
202                        'index' => $index,
203                        'data'  => $response->as_error(),
204                    )
205                );
206            }
207        }
208
209        if ( ! empty( $errors ) ) {
210            return new \WP_Error(
211                'rest_cannot_log_events',
212                __( 'Some events failed.' ),
213                array(
214                    'errors' => $errors,
215                )
216            );
217        }
218
219        return new \WP_REST_Response(
220            array(),
221            202 // Accepted. The request has been accepted for processing, but the processing has not been completed.
222        );
223    }
224}