Files
2026-07-02 15:54:39 -06:00

396 lines
12 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace Vrts\Features;
use Vrts\Models\Alert;
use Vrts\Models\Test;
use Vrts\Services\Test_Service;
class Service {
const DB_VERSION = '1.2';
const SERVICE = 'vrts_service';
const BASE_URL = VRTS_SERVICE_ENDPOINT;
/**
* Connect current website to VRTs Service.
*/
public static function connect_service() {
$option_name = self::SERVICE . '_version';
$installed_version = get_option( $option_name );
if ( self::DB_VERSION !== $installed_version ) {
update_option( $option_name, self::DB_VERSION );
}//end if
if ( ! self::is_connected() ) {
self::create_site();
}
if ( self::is_connected() && ! self::has_secret() ) {
self::create_secret();
}
if ( $installed_version && version_compare( $installed_version, '1.2', '<' ) ) {
$service_project_id = get_option( 'vrts_project_id' );
$service_api_route = 'sites/' . $service_project_id;
self::rest_service_request( $service_api_route, [], 'put' );
}
}
/**
* Rerty connection.
*/
public static function retry_connection() {
return static::create_site( true );
}
/**
* Helper to create site on service.
*
* @return bool
*/
private static function create_site() {
if ( static::is_connected() ) {
return;
}
$time = current_time( 'mysql' );
$rest_base_url = self::get_rest_url();
$service_api_route = 'sites';
$create_token = md5( 'verysecret' . $time );
$parameters = [
'create_token' => $create_token,
'rest_url' => $rest_base_url,
'admin_ajax_url' => admin_url( 'admin-ajax.php' ),
'requested_at' => $time,
];
if ( ! empty( get_option( 'vrts_project_id' ) ) && ! empty( get_option( 'vrts_project_token' ) ) ) {
$parameters['project_id'] = get_option( 'vrts_project_id' );
$parameters['project_token'] = get_option( 'vrts_project_token' );
$parameters['project_secret'] = get_option( 'vrts_project_secret' );
$parameters['tests'] = Test::get_all_service_test_ids();
}
$service_request = self::rest_service_request( $service_api_route, $parameters, 'post' );
delete_option( 'vrts_disconnected' );
if ( 201 === $service_request['status_code'] || 200 === $service_request['status_code'] ) {
$data = $service_request['response'];
update_option( 'vrts_project_id', $data['id'] );
update_option( 'vrts_project_token', $data['token'] );
update_option( 'vrts_project_secret', $data['secret'] ?? null );
Subscription::update_available_tests( $data['remaining_credits'], $data['total_credits'], $data['has_subscription'], $data['tier_id'] );
self::add_homepage_test();
return true;
} else {
update_option( 'vrts_disconnected', 1 );
}
return false;
}
/**
* Connect current website to VRTs Service.
*
* @param string $service_api_route the service api route.
* @param array $parameters the parameters.
* @param string $request_type the request type.
*/
public static function rest_service_request( $service_api_route, $parameters = [], $request_type = '' ) {
$request_url = self::BASE_URL . $service_api_route;
$service_project_id = get_option( 'vrts_project_id' );
$service_project_token = get_option( 'vrts_project_token' );
$response = [];
$args = [
'project_id' => $service_project_id,
'headers' => [
'Content-Type' => 'application/json; charset=utf-8',
'Authorization' => 'Bearer ' . $service_project_token,
],
'body' => wp_json_encode( $parameters ),
'data_format' => 'body',
];
// If project already created, attach project id and service token.
if ( $service_project_id && $service_project_token ) {
$args['project_id'] = $service_project_id;
$args['headers']['Authorization'] = 'Bearer ' . $service_project_token;
}
add_filter( 'http_headers_useragent', [ static::class, 'set_user_agent' ], 10 );
switch ( $request_type ) {
case 'get':
$args = [
'method' => 'GET',
'project_id' => $service_project_id,
'headers' => [
'Authorization' => 'Bearer ' . $service_project_token,
],
'body' => $parameters,
'data_format' => 'body',
];
$data = wp_remote_post( $request_url, $args );
$response = [
'response' => json_decode( wp_remote_retrieve_body( $data ), true ),
'status_code' => wp_remote_retrieve_response_code( $data ),
];
break;
case 'delete':
$args['method'] = 'DELETE';
$data = wp_remote_post( $request_url, $args );
break;
case 'put':
$args['method'] = 'PUT';
$data = wp_remote_post( $request_url, $args );
break;
default:
$data = wp_remote_post( $request_url, $args );
$response = [
'response' => json_decode( wp_remote_retrieve_body( $data ), true ),
'status_code' => wp_remote_retrieve_response_code( $data ),
];
break;
}//end switch
if ( empty( $response ) ) {
$response = [
'response' => $data,
'status_code' => wp_remote_retrieve_response_code( $data ),
];
}
remove_filter( 'http_headers_useragent', [ static::class, 'set_user_agent' ], 10 );
return $response;
}
/**
* Send request to server to resume test.
*
* @param int $post_id the post id.
*/
public static function resume_test( $post_id ) {
$service_test_id = Test::get_service_test_id_by_post_id( $post_id );
if ( $service_test_id ) {
$service_api_route = 'tests/' . $service_test_id . '/resume';
$response = self::rest_service_request( $service_api_route, [], 'post' );
}
}
/**
* Send request to server to delete test.
*
* @param int $service_test_id the service test id.
*/
public static function delete_test( $service_test_id ) {
$service_api_route = 'tests/' . $service_test_id;
$response = self::rest_service_request( $service_api_route, [], 'delete' );
return 200 === $response['status_code'];
}
/**
* Send request to server to update test.
*
* @param int $service_test_id the service test id.
* @param array $data the data.
*/
public static function update_test( $service_test_id, $data ) {
$service_api_route = 'tests/' . $service_test_id;
$response = self::rest_service_request( $service_api_route, $data, 'put' );
return 200 === $response['status_code'];
}
/**
* Send request to server to resume tests.
*/
public static function resume_tests() {
$service_project_id = get_option( 'vrts_project_id' );
$service_api_route = 'sites/' . $service_project_id;
self::rest_service_request( $service_api_route . '/resume', [], 'post' );
}
/**
* Add homepage as a default test.
*/
public static function add_homepage_test() {
$option_name = 'vrts_homepage_added';
$homepage_added = get_option( $option_name );
// If plugin was previously activated, dont add homepage again.
if ( ! $homepage_added ) {
$homepage_id = get_option( 'page_on_front' );
// Save data to custom database table.
$test_service = new Test_Service();
$test_service->create_test( $homepage_id );
update_option( $option_name, 1 );
}
}
/**
* Get rest url for default language if WPML is installed.
*/
private static function get_rest_url() {
// Exlusion for WPML installations.
global $sitepress;
$rest_url = rest_url( 'vrts/v1/service' );
if ( $sitepress ) {
// WPML Get languages.
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- It's ok.
$wpml_current_lang = apply_filters( 'wpml_current_language', null );
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- It's ok.
$wpml_default_lang = apply_filters( 'wpml_default_language', null );
// If current language is not default, switch to default language to get the rest url.
if ( $wpml_current_lang !== $wpml_default_lang ) {
$sitepress->switch_lang( $wpml_default_lang );
$rest_url = rest_url( 'vrts/v1/service' );
// Switch back to the current language.
$sitepress->switch_lang( $wpml_current_lang );
}
}
return $rest_url;
}
/**
* Run manual tests.
*
* @param string[] $service_test_ids the service test ids.
* @param array $options the options.
*/
public static function run_manual_tests( $service_test_ids, $options = [] ) {
$service_project_id = get_option( 'vrts_project_id' );
$service_api_route = 'sites/' . $service_project_id . '/trigger';
return self::rest_service_request( $service_api_route, array_merge( $options, [
'ids' => $service_test_ids,
]), 'post' );
}
/**
* Mark alert as false positive.
*
* @param int $alert_id Alert id.
*/
public static function mark_alert_as_false_positive( $alert_id ) {
$alert = Alert::get_item( $alert_id );
$service_api_route = 'tests/' . $alert->screenshot_test_id . '/false-positives';
return self::rest_service_request( $service_api_route, [
'comparison_id' => $alert->comparison_id,
], 'post' );
}
/**
* Unmark alert as false positive.
*
* @param int $alert_id Alert id.
*/
public static function unmark_alert_as_false_positive( $alert_id ) {
$alert = Alert::get_item( $alert_id );
$service_api_route = 'tests/' . $alert->screenshot_test_id . '/false-positives/' . $alert->comparison_id;
return self::rest_service_request( $service_api_route, [], 'delete' );
}
/**
* Get project id from the service.
*/
public static function fetch_updates() {
$service_project_id = get_option( 'vrts_project_id' );
$service_api_route = 'sites/' . $service_project_id . '/updates';
return self::rest_service_request( $service_api_route, [], 'get' );
}
/**
* Get test runs from the service.
*
* @param string[] $test_run_ids the test run ids.
*/
public static function fetch_test_runs( $test_run_ids ) {
$service_project_id = get_option( 'vrts_project_id' );
$service_api_route = 'sites/' . $service_project_id . '/runs';
$service_api_route = add_query_arg( 'ids', implode( ',', $test_run_ids ), $service_api_route );
return self::rest_service_request( $service_api_route, [], 'get' );
}
/**
* Delete project from the service.
*/
public static function disconnect_service() {
$service_project_id = get_option( 'vrts_project_id' );
$service_api_route = 'sites/' . $service_project_id;
$response = self::rest_service_request( $service_api_route, [], 'delete' );
if ( 200 === $response['status_code'] ) {
update_option( 'vrts_disconnected', 1 );
}
}
/**
* Drop the database table for tests.
*/
public static function delete_option() {
delete_option( 'vrts_project_id' );
delete_option( 'vrts_project_token' );
delete_option( 'vrts_project_secret' );
delete_option( 'vrts_create_token' );
delete_option( 'vrts_access_token' );
delete_option( 'vrts_homepage_added' );
delete_option( 'vrts_site_urls' );
delete_option( 'vrts_connection_inactive' );
delete_option( self::SERVICE . '_version' );
}
/**
* Check if external service was able to connect
*/
public static function is_connected() {
return ! (bool) get_option( 'vrts_disconnected' ) && (bool) get_option( 'vrts_project_id' ) && (bool) get_option( 'vrts_project_token' );
}
/**
* Check if secret was created
*/
public static function has_secret() {
return (bool) get_option( 'vrts_project_secret' );
}
/**
* Set user agent for the request.
*
* @param string $user_agent the user agent.
*/
public static function set_user_agent( $user_agent ) {
if ( function_exists( 'vrts' ) ) {
return 'VRTs/' . vrts()->get_plugin_info( 'version' ) . ';' . $user_agent;
} else {
return $user_agent;
}
}
/**
* Create secret for the project
*/
private static function create_secret() {
$service_project_id = get_option( 'vrts_project_id' );
$service_api_route = 'sites/' . $service_project_id . '/secret';
$service_request = self::rest_service_request( $service_api_route, [], 'post' );
if ( 200 === $service_request['status_code'] ) {
update_option( 'vrts_project_secret', $service_request['response']['secret'] ?? null );
}
}
}