• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / wordpress-seo / c710c31289d8b4f262939f3a3ca36271a63cce16

13 Mar 2026 09:22AM UTC coverage: 53.449% (+2.5%) from 50.9%
c710c31289d8b4f262939f3a3ca36271a63cce16

push

github

leonidasmi
Merge branch 'trunk' into feature/task-list-phase-3

9052 of 16649 branches covered (54.37%)

Branch coverage included in aggregate %.

828 of 870 new or added lines in 44 files covered. (95.17%)

718 existing lines in 33 files now uncovered.

34354 of 64561 relevant lines covered (53.21%)

46593.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/ai-generator/user-interface/get-usage-route.php
1
<?php
2

3
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
4
namespace Yoast\WP\SEO\AI_Generator\User_Interface;
5

6
use WP_REST_Response;
7
use WPSEO_Addon_Manager;
8
use Yoast\WP\SEO\AI_Authorization\Application\Token_Manager;
9
use Yoast\WP\SEO\AI_HTTP_Request\Application\Request_Handler;
10
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Remote_Request_Exception;
11
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Too_Many_Requests_Exception;
12
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\WP_Request_Exception;
13
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Request;
14
use Yoast\WP\SEO\Conditionals\AI_Conditional;
15
use Yoast\WP\SEO\Main;
16
use Yoast\WP\SEO\Routes\Route_Interface;
17

18
/**
19
 * Registers a route to get suggestions from the AI API
20
 *
21
 * @makePublic
22
 *
23
 * @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
24
 */
25
class Get_Usage_Route implements Route_Interface {
26

27
        use Route_Permission_Trait;
28

29
                /**
30
                 *  The namespace for this route.
31
                 *
32
                 * @var string
33
                 */
34
        public const ROUTE_NAMESPACE = Main::API_V1_NAMESPACE;
35

36
        /**
37
         *  The prefix for this route.
38
         *
39
         * @var string
40
         */
41
        public const ROUTE_PREFIX = '/ai_generator/get_usage';
42

43
        /**
44
         * The token manager instance.
45
         *
46
         * @var Token_Manager
47
         */
48
        private $token_manager;
49

50
        /**
51
         * The request handler instance.
52
         *
53
         * @var Request_Handler
54
         */
55
        private $request_handler;
56

57
        /**
58
         * Represents the add-on manager.
59
         *
60
         * @var WPSEO_Addon_Manager
61
         */
62
        private $addon_manager;
63

64
        /**
65
         * Returns the conditionals based in which this loadable should be active.
66
         *
67
         * @return array<string> The conditionals.
68
         */
UNCOV
69
        public static function get_conditionals() {
×
UNCOV
70
                return [ AI_Conditional::class ];
×
71
        }
72

73
        /**
74
         * Class constructor.
75
         *
76
         * @param Token_Manager       $token_manager   The token manager instance.
77
         * @param Request_Handler     $request_handler The request handler instance.
78
         * @param WPSEO_Addon_Manager $addon_manager   The add-on manager instance.
79
         */
UNCOV
80
        public function __construct( Token_Manager $token_manager, Request_Handler $request_handler, WPSEO_Addon_Manager $addon_manager ) {
×
UNCOV
81
                $this->addon_manager   = $addon_manager;
×
UNCOV
82
                $this->token_manager   = $token_manager;
×
UNCOV
83
                $this->request_handler = $request_handler;
×
84
        }
85

86
        /**
87
         * Registers routes with WordPress.
88
         *
89
         * @return void
90
         */
UNCOV
91
        public function register_routes() {
×
UNCOV
92
                \register_rest_route(
×
UNCOV
93
                        self::ROUTE_NAMESPACE,
×
UNCOV
94
                        self::ROUTE_PREFIX,
×
UNCOV
95
                        [
×
UNCOV
96
                                'methods'             => 'POST',
×
UNCOV
97
                                'args'                => [
×
UNCOV
98
                                        'is_woo_product_entity' => [
×
UNCOV
99
                                                'type'        => 'boolean',
×
UNCOV
100
                                                'default'     => false,
×
UNCOV
101
                                        ],
×
UNCOV
102
                                ],
×
UNCOV
103
                                'callback'            => [ $this, 'get_usage' ],
×
UNCOV
104
                                'permission_callback' => [ $this, 'check_permissions' ],
×
UNCOV
105
                        ],
×
UNCOV
106
                );
×
107
        }
108

109
        /**
110
         * Runs the callback that gets the monthly usage of the user.
111
         *
112
         * @param WP_REST_Request $request The request object.
113
         *
114
         * @return WP_REST_Response The response of the callback action.
115
         */
UNCOV
116
        public function get_usage( $request ): WP_REST_Response {
×
UNCOV
117
                $is_woo_product_entity = $request->get_param( 'is_woo_product_entity' );
×
UNCOV
118
                $user                  = \wp_get_current_user();
×
119
                try {
UNCOV
120
                        $token           = $this->token_manager->get_or_request_access_token( $user );
×
UNCOV
121
                        $request_headers = [
×
UNCOV
122
                                'Authorization' => "Bearer $token",
×
UNCOV
123
                        ];
×
UNCOV
124
                        $action_path     = $this->get_action_path( $is_woo_product_entity );
×
UNCOV
125
                        $response        = $this->request_handler->handle( new Request( $action_path, [], $request_headers, false ) );
×
UNCOV
126
                        $data            = \json_decode( $response->get_body() );
×
UNCOV
127
                } catch ( Remote_Request_Exception | WP_Request_Exception $e ) {
×
UNCOV
128
                        $message = [
×
UNCOV
129
                                'errorMessage'    => $e->getMessage(),
×
UNCOV
130
                                'errorIdentifier' => $e->get_error_identifier(),
×
UNCOV
131
                                'errorCode'       => $e->getCode(),
×
UNCOV
132
                        ];
×
UNCOV
133
                        if ( $e instanceof Too_Many_Requests_Exception ) {
×
UNCOV
134
                                $message['missingLicenses'] = $e->get_missing_licenses();
×
135
                        }
UNCOV
136
                        return new WP_REST_Response(
×
UNCOV
137
                                $message,
×
UNCOV
138
                                $e->getCode(),
×
UNCOV
139
                        );
×
140
                }
141

UNCOV
142
                return new WP_REST_Response( $data );
×
143
        }
144

145
        /**
146
         * Get action path for the request.
147
         *
148
         * @param bool $is_woo_product_entity Whether the request is for a WooCommerce product entity.
149
         *
150
         * @return string The action path.
151
         */
UNCOV
152
        public function get_action_path( $is_woo_product_entity = false ): string {
×
UNCOV
153
                $unlimited = '/usage/' . \gmdate( 'Y-m' );
×
UNCOV
154
                if ( $is_woo_product_entity && $this->addon_manager->has_valid_subscription( WPSEO_Addon_Manager::WOOCOMMERCE_SLUG ) ) {
×
UNCOV
155
                        return $unlimited;
×
156
                }
UNCOV
157
                if ( ! $is_woo_product_entity && $this->addon_manager->has_valid_subscription( WPSEO_Addon_Manager::PREMIUM_SLUG ) ) {
×
UNCOV
158
                        return $unlimited;
×
159
                }
UNCOV
160
                return '/usage/free-usages';
×
161
        }
162
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc