Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 50
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
HostingUapiClient
0.00% covered (danger)
0.00%
0 / 50
0.00% covered (danger)
0.00%
0 / 3
380
0.00% covered (danger)
0.00%
0 / 1
 put_site_performance_redis
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
72
 extract_customer_error
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
72
 snippet
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace NewfoldLabs\WP\Module\Performance\Helpers;
4
5/**
6 * Minimal Hosting UAPI client for site-scoped endpoints.
7 */
8final class HostingUapiClient {
9
10    /**
11     * PUT /v1/sites/{site_id}/performance/redis
12     *
13     * @param string $huapi_jwt HUAPI JWT (from Hiive customer payload).
14     * @param string $site_id   HAL site id (digits).
15     * @param bool   $enabled   Desired redis enablement.
16     * @return true|\WP_Error
17     */
18    public static function put_site_performance_redis( $huapi_jwt, $site_id, $enabled ) {
19        $huapi_jwt = (string) $huapi_jwt;
20        $site_id   = (string) $site_id;
21
22        if ( '' === $huapi_jwt || '' === $site_id ) {
23            return new \WP_Error( 'nfd_hosting_uapi_error', __( 'Could not enable object cache right now. Please try again later.', 'wp-module-performance' ) );
24        }
25
26        $base = SiteApisConfig::hosting_uapi_base_url();
27        $url  = $base . 'v1/sites/' . rawurlencode( $site_id ) . '/performance/redis';
28
29        $body = array( 'state' => (bool) $enabled );
30
31        /**
32         * Allow adjusting request body for environments that require 0/1 instead of JSON booleans.
33         *
34         * @param array  $body
35         * @param string $site_id
36         */
37        $body = apply_filters( 'newfold_performance_hosting_uapi_redis_toggle_body', $body, $site_id );
38
39        $args = array(
40            'method'  => 'PUT',
41            'timeout' => SiteApisConfig::hosting_uapi_request_timeout_seconds(),
42            'headers' => array(
43                'Content-Type'  => 'application/json',
44                'Authorization' => 'Bearer ' . $huapi_jwt,
45            ),
46            'body'    => wp_json_encode( $body ),
47        );
48
49        $response = wp_remote_request( $url, $args );
50
51        if ( is_wp_error( $response ) ) {
52            return $response;
53        }
54
55        $code = (int) wp_remote_retrieve_response_code( $response );
56        $raw  = (string) wp_remote_retrieve_body( $response );
57
58        if ( $code < 200 || $code >= 300 ) {
59            $data           = json_decode( $raw, true );
60            $customer_error = is_array( $data ) ? self::extract_customer_error( $data ) : null;
61
62            $err = new \WP_Error(
63                'nfd_hosting_uapi_error',
64                __( 'Could not enable object cache right now. Please try again later.', 'wp-module-performance' ),
65                array(
66                    'status'         => $code,
67                    'body'           => $raw,
68                    'customer_error' => $customer_error,
69                    'decoded'        => is_array( $data ) ? $data : null,
70                )
71            );
72
73            /**
74             * Filter WP_Error for Hosting UAPI failures (map/customize).
75             *
76             * @param \WP_Error $err
77             * @param int       $code
78             * @param string    $raw
79             */
80            return apply_filters( 'newfold_performance_hosting_uapi_redis_toggle_error', $err, $code, $raw );
81        }
82
83        return true;
84    }
85
86    /**
87     * Extract a stable customer-facing error string from a decoded JSON body when present.
88     *
89     * @param array $data Decoded JSON.
90     */
91    private static function extract_customer_error( array $data ): ?string {
92        // Common shapes: { "customer_error": "..." } or nested under error/details.
93        if ( isset( $data['customer_error'] ) && is_string( $data['customer_error'] ) && '' !== $data['customer_error'] ) {
94            return $data['customer_error'];
95        }
96
97        if ( isset( $data['error'] ) && is_array( $data['error'] ) ) {
98            $err = $data['error'];
99            if ( isset( $err['customer_error'] ) && is_string( $err['customer_error'] ) ) {
100                return $err['customer_error'];
101            }
102        }
103
104        return null;
105    }
106
107    /**
108     * Truncate raw response text for error messages.
109     *
110     * @param string $raw Response body.
111     * @return string Trimmed substring, max 240 characters.
112     */
113    private static function snippet( string $raw ): string {
114        $raw = trim( $raw );
115        if ( '' === $raw ) {
116            return '';
117        }
118
119        return function_exists( 'mb_substr' )
120            ? (string) mb_substr( $raw, 0, 240 )
121            : (string) substr( $raw, 0, 240 );
122    }
123}