reason = $reason; $this->status = (int) $status; } /** * Machine-readable reason. * * @return string */ public function reason() { return $this->reason; } /** * HTTP status from Google. * * @return int */ public function status() { return $this->status; } /** * Converts the exception for a REST response. * * @return \WP_Error */ public function to_wp_error() { if ( self::DISABLED === $this->reason ) { $http = 403; } elseif ( in_array( $this->reason, array( self::NOT_CONFIGURED, self::UNSUPPORTED, self::MISSING_SCOPE ), true ) ) { $http = 409; } else { $http = 502; } return new \WP_Error( 'turtlereport_' . $this->reason, $this->getMessage(), array( 'status' => $http, 'reason' => $this->reason, ) ); } /** * Builds an exception from a Google API error response. * * @param int $status HTTP status. * @param array $body Decoded JSON body. * @return self */ public static function from_api( $status, array $body ) { $message = isset( $body['error'] ) && is_array( $body['error'] ) && isset( $body['error']['message'] ) && is_string( $body['error']['message'] ) ? $body['error']['message'] : ''; if ( '' === $message ) { /* translators: %d: HTTP status code. */ $message = sprintf( __( 'Google returned an error (%d).', 'turtlereport' ), $status ); } $apis = array( 'Search Console API' => 'Google Search Console API', 'Analytics Data API' => 'Google Analytics Data API', 'Analytics Admin API' => 'Google Analytics Admin API', 'Account Management API' => 'My Business Account Management API', 'Business Information API' => 'My Business Business Information API', 'Business Profile Performance API' => 'Business Profile Performance API', ); if ( preg_match( '/has not been used|is disabled|it is disabled/i', $message ) ) { foreach ( $apis as $needle => $name ) { if ( false !== strpos( $message, $needle ) ) { return new self( /* translators: %s: Google API name. */ sprintf( __( 'The %s is not enabled in this connection\'s Google Cloud project. Enable it in APIs & Services, wait a minute, then try again.', 'turtlereport' ), $name ), self::API_DISABLED, $status ); } } } if ( 429 === (int) $status || preg_match( '/quota/i', $message ) ) { return new self( __( 'Google\'s API quota was reached. For Business Profile this usually means Google hasn\'t approved API access for the Cloud project yet.', 'turtlereport' ), self::QUOTA, $status ); } if ( preg_match( '/insufficient authentication scopes/i', $message ) ) { return new self( __( 'A Google permission is missing. Reconnect this connection and tick every permission.', 'turtlereport' ), self::MISSING_SCOPE, $status ); } if ( preg_match( '/sufficient permission|does not have permission|permission denied|User does not have/i', $message ) || 403 === (int) $status ) { return new self( __( 'This Google account or service account can\'t read this property. Add that account as a user in Search Console, GA4 or Business Profile, then try again.', 'turtlereport' ), self::PERMISSION, $status ); } return new self( $message, self::GOOGLE, $status ); } /** * Builds an exception from an OAuth token endpoint error. * * @param int $status HTTP status. * @param array $body Decoded JSON body. * @return self */ public static function from_token_error( $status, array $body ) { $error = isset( $body['error'] ) && is_string( $body['error'] ) ? $body['error'] : ''; switch ( $error ) { case 'invalid_grant': return new self( __( 'Google sign-in for this connection expired or was revoked. Reconnect it. If this keeps happening every 7 days, publish your OAuth app (Google Auth Platform → Audience → Publish app).', 'turtlereport' ), self::RECONNECT, $status ); case 'invalid_client': case 'unauthorized_client': return new self( __( 'Google rejected the Client ID or secret. Check both values in Google Auth Platform → Clients.', 'turtlereport' ), self::RECONNECT, $status ); case 'redirect_uri_mismatch': return new self( __( 'The redirect URI is not registered on this OAuth client. Copy it from the Connections screen into Authorized redirect URIs.', 'turtlereport' ), self::INVALID, $status ); } $desc = isset( $body['error_description'] ) ? (string) $body['error_description'] : $error; /* translators: %s: error from Google. */ return new self( sprintf( __( 'Google sign-in failed: %s', 'turtlereport' ), '' !== $desc ? $desc : (string) $status ), self::GOOGLE, $status ); } }