Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 75
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
WC_Analytics_Tracking_Proxy
0.00% covered (danger)
0.00%
0 / 74
0.00% covered (danger)
0.00%
0 / 3
182
0.00% covered (danger)
0.00%
0 / 1
 register_routes
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
 track_events
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 1
132
 get_item_schema
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * REST API: WC_Analytics_Tracking_Proxy class
4 *
5 * @package automattic/woocommerce-analytics
6 */
7
8namespace Automattic\Woocommerce_Analytics;
9
10defined( 'ABSPATH' ) || exit;
11
12/**
13 * Class to handle tracking events via the REST API
14 *
15 * @since 0.7.0
16 */
17class WC_Analytics_Tracking_Proxy extends \WC_REST_Controller {
18
19    /**
20     * Endpoint namespace.
21     *
22     * @var string
23     */
24    protected $namespace = 'woocommerce-analytics/v1';
25
26    /**
27     * Route base.
28     *
29     * @var string
30     */
31    protected $rest_base = 'track';
32
33    /**
34     * Register the routes for tracking.
35     */
36    public function register_routes() {
37        register_rest_route(
38            $this->namespace,
39            '/' . $this->rest_base,
40            array(
41                array(
42                    'methods'             => \WP_REST_Server::CREATABLE,
43                    'callback'            => array( $this, 'track_events' ),
44                    'permission_callback' => '__return_true', // no need to check permissions
45                    'schema'              => array( $this, 'get_public_item_schema' ),
46                ),
47            )
48        );
49    }
50
51    /**
52     * Track events.
53     *
54     * @param \WP_REST_Request $request Full data about the request.
55     * @return \WP_REST_Response|\WP_Error Response object on success, or WP_Error object on failure.
56     */
57    public function track_events( $request ) {
58        // Check consent before processing any events
59        if ( ! Consent_Manager::has_analytics_consent() ) {
60            return new \WP_REST_Response(
61                array(
62                    'success' => true,
63                    'message' => 'Events skipped due to lack of analytics consent',
64                    'results' => array(),
65                ),
66                200
67            );
68        }
69
70        $events = $request->get_json_params();
71
72        if ( ! is_array( $events ) || ( isset( $events['event_name'] ) ) ) {
73            // If $events is a single event (associative array), wrap it in an array.
74            $events = array( $events );
75        }
76
77        $results    = array();
78        $has_errors = false;
79
80        foreach ( $events as $index => $event ) {
81            // Validate event structure.
82            if ( empty( $event ) || ! is_array( $event ) ) {
83                $results[ $index ] = array(
84                    'success' => false,
85                    'error'   => 'Invalid event format',
86                );
87                $has_errors        = true;
88                continue;
89            }
90
91            // Validate event name and properties.
92            $event_name = $event['event_name'] ?? null;
93            $properties = $event['properties'] ?? array();
94            if ( ! $event_name || ! is_array( $properties ) ) {
95                $results[ $index ] = array(
96                    'success' => false,
97                    'error'   => 'Missing event_name or invalid properties',
98                );
99                $has_errors        = true;
100                continue;
101            }
102
103            $result = WC_Analytics_Tracking::record_event( $event_name, $properties );
104
105            if ( is_wp_error( $result ) ) {
106                $results[ $index ] = array(
107                    'success' => false,
108                    'error'   => $result->get_error_message(),
109                );
110                $has_errors        = true;
111                continue;
112            }
113
114            $results[ $index ] = array( 'success' => true );
115        }
116
117        $response_data = array(
118            'success' => ! $has_errors,
119            'results' => $results,
120        );
121
122        return new \WP_REST_Response( $response_data, $has_errors ? 207 : 200 );
123    }
124
125    /**
126     * Get the schema for tracking events.
127     *
128     * @return array
129     */
130    public function get_item_schema() {
131        $schema = array(
132            '$schema' => 'http://json-schema.org/draft-04/schema#',
133            'title'   => 'tracking_events',
134            'type'    => 'array',
135            'items'   => array(
136                'type'       => 'object',
137                'properties' => array(
138                    'event_name' => array(
139                        'type' => 'string',
140                    ),
141                    'properties' => array(
142                        'type' => 'object',
143                    ),
144                ),
145            ),
146        );
147
148        return $this->add_additional_fields_schema( $schema );
149    }
150}