updated plugin Jetpack Protect
version 3.0.2
This commit is contained in:
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* Package description here
|
||||
*
|
||||
* @package automattic/jetpack-explat
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack;
|
||||
|
||||
use Automattic\Jetpack\Connection\Rest_Authentication;
|
||||
use Automattic\Jetpack\ExPlat\REST_Controller;
|
||||
|
||||
/**
|
||||
* Class description.
|
||||
*/
|
||||
class ExPlat {
|
||||
|
||||
/**
|
||||
* ExPlat package version
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PACKAGE_VERSION = '0.1.7';
|
||||
|
||||
/**
|
||||
* Initializer.
|
||||
* Used to configure the ExPlat package
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init() {
|
||||
if ( did_action( 'jetpack_explat_initialized' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up the REST authentication hooks.
|
||||
Rest_Authentication::init();
|
||||
|
||||
add_action( 'rest_api_init', array( new REST_Controller(), 'register_rest_routes' ) );
|
||||
|
||||
// Runs right after the Jetpack ExPlat package is initialized.
|
||||
do_action( 'jetpack_explat_initialized' );
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/**
|
||||
* The ExPlat Rest Controller class.
|
||||
* Registers the REST routes for ExPlat backend
|
||||
*
|
||||
* @package automattic/jetpack-explat
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack\ExPlat;
|
||||
|
||||
use Automattic\Jetpack\Connection\Client;
|
||||
use Automattic\Jetpack\Connection\Manager as Jetpack_Connection;
|
||||
use WP_Error;
|
||||
use WP_REST_Server;
|
||||
|
||||
/**
|
||||
* Registers general REST routes for ExPlat.
|
||||
*/
|
||||
class REST_Controller {
|
||||
/**
|
||||
* Namespace for the REST API.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $namespace = 'jetpack/v4/explat';
|
||||
|
||||
/**
|
||||
* Current version of the ExPlat API and components
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const EXPLAT_API_VERSION = '0.1.0';
|
||||
|
||||
/**
|
||||
* Base API URI for WordPress.com
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const WPCOM_API_BASE_URL = 'https://public-api.wordpress.com/wpcom/v2';
|
||||
|
||||
/**
|
||||
* Registers the REST routes.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public function register_rest_routes() {
|
||||
register_rest_route(
|
||||
static::$namespace,
|
||||
'assignments',
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_assignments' ),
|
||||
'permission_callback' => '__return_true',
|
||||
'args' => array(
|
||||
'experiment_name' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'anon_id' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'as_connected_user' => array(
|
||||
'type' => 'boolean',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assignments for a given experiment and anon_id
|
||||
*
|
||||
* @param WP_REST_Request $request The REST request object.
|
||||
* @return WP_REST_Response
|
||||
*/
|
||||
public function get_assignments( $request ) {
|
||||
$response = null;
|
||||
$is_user_connected = ( new Jetpack_Connection() )->is_user_connected();
|
||||
$request_path = '/experiments/' . self::EXPLAT_API_VERSION . '/assignments/jetpack';
|
||||
$args = array(
|
||||
'experiment_name' => $request['experiment_name'],
|
||||
'anon_id' => $request['anon_id'],
|
||||
);
|
||||
|
||||
if ( $request['as_connected_user'] && $is_user_connected ) {
|
||||
$response = Client::wpcom_json_api_request_as_user(
|
||||
add_query_arg( $args, $request_path ),
|
||||
'v2'
|
||||
);
|
||||
} else {
|
||||
$response = wp_remote_get(
|
||||
add_query_arg( $args, self::WPCOM_API_BASE_URL . $request_path )
|
||||
);
|
||||
}
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return new WP_Error(
|
||||
'wp_error_fetching_assignment',
|
||||
$response->get_error_message(),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( 200 !== $response_code ) {
|
||||
return new WP_Error(
|
||||
'http_error_fetching_assignment',
|
||||
wp_remote_retrieve_response_message( $response ),
|
||||
array( 'status' => $response_code )
|
||||
);
|
||||
}
|
||||
|
||||
return rest_ensure_response(
|
||||
json_decode( wp_remote_retrieve_body( $response ), true )
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import cookie from 'cookie';
|
||||
|
||||
let initializeAnonIdPromise: Promise< string | null > | null = null;
|
||||
const anonIdPollingIntervalMilliseconds = 50;
|
||||
const anonIdPollingIntervalMaxAttempts = 100; // 50 * 100 = 5000 = 5 seconds
|
||||
|
||||
/**
|
||||
* Gather w.js anonymous cookie, tk_ai
|
||||
*
|
||||
* @return {?string} The anonymous cookie value, or null if it doesn't exist
|
||||
*/
|
||||
export const readAnonCookie = (): string | null => {
|
||||
return cookie.parse( document.cookie ).tk_ai || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the anonId:
|
||||
* - Polls for AnonId receival
|
||||
* - Should only be called once at startup
|
||||
* - Happens to be safe to call multiple times if it is necessary to reset the anonId - something like this was necessary for testing.
|
||||
*
|
||||
* This purely for boot-time initialization, in usual circumstances it will be retrieved within 100-300ms, it happens in parallel booting
|
||||
* so should only delay experiment loading that much for boot-time experiments. In some circumstances such as a very slow connection this
|
||||
* can take a lot longer.
|
||||
*
|
||||
* The state of initializeAnonIdPromise should be used rather than the return of this function.
|
||||
* The return is only avaliable to make this easier to test.
|
||||
*
|
||||
* Throws on error.
|
||||
*
|
||||
* @return {Promise<string | null>} The anonymous cookie value, or null if it doesn't exist
|
||||
*/
|
||||
export const initializeAnonId = async (): Promise< string | null > => {
|
||||
let attempt = 0;
|
||||
initializeAnonIdPromise = new Promise( res => {
|
||||
const poll = () => {
|
||||
const anonId = readAnonCookie();
|
||||
if ( typeof anonId === 'string' && anonId !== '' ) {
|
||||
res( anonId );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( anonIdPollingIntervalMaxAttempts - 1 <= attempt ) {
|
||||
res( null );
|
||||
return;
|
||||
}
|
||||
attempt += 1;
|
||||
setTimeout( poll, anonIdPollingIntervalMilliseconds );
|
||||
};
|
||||
poll();
|
||||
} );
|
||||
|
||||
return initializeAnonIdPromise;
|
||||
};
|
||||
|
||||
export const getAnonId = async (): Promise< string | null > => {
|
||||
return await initializeAnonIdPromise;
|
||||
};
|
@ -0,0 +1,29 @@
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
import { addQueryArgs } from '@wordpress/url';
|
||||
|
||||
const fetchExperimentAssignment =
|
||||
( asConnectedUser = false ) =>
|
||||
async ( {
|
||||
experimentName,
|
||||
anonId,
|
||||
}: {
|
||||
experimentName: string;
|
||||
anonId: string | null;
|
||||
} ): Promise< unknown > => {
|
||||
if ( ! anonId ) {
|
||||
throw new Error( `Tracking is disabled, can't fetch experimentAssignment` );
|
||||
}
|
||||
|
||||
const params = {
|
||||
experiment_name: experimentName,
|
||||
anon_id: anonId ?? undefined,
|
||||
as_connected_user: asConnectedUser,
|
||||
};
|
||||
|
||||
const assignmentsRequestUrl = addQueryArgs( 'jetpack/v4/explat/assignments', params );
|
||||
|
||||
return await apiFetch( { path: assignmentsRequestUrl } );
|
||||
};
|
||||
|
||||
export const fetchExperimentAssignmentAnonymously = fetchExperimentAssignment( false );
|
||||
export const fetchExperimentAssignmentWithAuth = fetchExperimentAssignment( true );
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { isDevelopmentMode } from './utils';
|
||||
|
||||
export const logError = ( error: Record< string, string > & { message: string } ): void => {
|
||||
const onLoggingError = ( e: unknown ) => {
|
||||
if ( isDevelopmentMode ) {
|
||||
console.error( '[ExPlat] Unable to send error to server:', e ); // eslint-disable-line no-console
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const { message, ...properties } = error;
|
||||
const logStashError = {
|
||||
message,
|
||||
properties: {
|
||||
...properties,
|
||||
context: 'explat',
|
||||
explat_client: 'jetpack',
|
||||
},
|
||||
};
|
||||
|
||||
if ( isDevelopmentMode ) {
|
||||
console.error( '[ExPlat] ', error.message, error ); // eslint-disable-line no-console
|
||||
} else {
|
||||
const body = new window.FormData();
|
||||
body.append( 'error', JSON.stringify( logStashError ) );
|
||||
window
|
||||
.fetch( 'https://public-api.wordpress.com/rest/v1.1/js-error', {
|
||||
method: 'POST',
|
||||
body,
|
||||
} )
|
||||
.catch( onLoggingError );
|
||||
}
|
||||
} catch ( e ) {
|
||||
onLoggingError( e );
|
||||
}
|
||||
};
|
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createExPlatClient } from '@automattic/explat-client';
|
||||
import createExPlatClientReactHelpers from '@automattic/explat-client-react-helpers';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getAnonId, initializeAnonId } from './anon';
|
||||
import {
|
||||
fetchExperimentAssignmentAnonymously,
|
||||
fetchExperimentAssignmentWithAuth,
|
||||
} from './assignment';
|
||||
import { logError } from './error';
|
||||
import { isDevelopmentMode } from './utils';
|
||||
|
||||
export const initializeExPlat = (): void => {
|
||||
initializeAnonId().catch( e => logError( { message: e.message } ) );
|
||||
};
|
||||
|
||||
initializeExPlat();
|
||||
|
||||
const exPlatClient = createExPlatClient( {
|
||||
fetchExperimentAssignment: fetchExperimentAssignmentAnonymously,
|
||||
getAnonId,
|
||||
logError,
|
||||
isDevelopmentMode,
|
||||
} );
|
||||
|
||||
export const { loadExperimentAssignment, dangerouslyGetExperimentAssignment } = exPlatClient;
|
||||
|
||||
export const { useExperiment, Experiment, ProvideExperimentData } =
|
||||
createExPlatClientReactHelpers( exPlatClient );
|
||||
|
||||
const exPlatClientWithAuth = createExPlatClient( {
|
||||
fetchExperimentAssignment: fetchExperimentAssignmentWithAuth,
|
||||
getAnonId,
|
||||
logError,
|
||||
isDevelopmentMode,
|
||||
} );
|
||||
|
||||
export const {
|
||||
loadExperimentAssignment: loadExperimentAssignmentWithAuth,
|
||||
dangerouslyGetExperimentAssignment: dangerouslyGetExperimentAssignmentWithAuth,
|
||||
} = exPlatClientWithAuth;
|
||||
|
||||
export const {
|
||||
useExperiment: useExperimentWithAuth,
|
||||
Experiment: ExperimentWithAuth,
|
||||
ProvideExperimentData: ProvideExperimentDataWithAuth,
|
||||
} = createExPlatClientReactHelpers( exPlatClientWithAuth );
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Boolean determining if environment is development.
|
||||
*/
|
||||
export const isDevelopmentMode = process.env.NODE_ENV === 'development';
|
Reference in New Issue
Block a user