Skip to content

Commit

Permalink
Merge work from Omeda API
Browse files Browse the repository at this point in the history
The update enforces strict typing in `class-omeda-client.php` and introduces new constants for API endpoints. There are also updates to debug mode activation rules and an error handling mechanism for production and staging environments. Additional private methods for getting headers and storing customers were introduced, improving flow organization and readability. The changes were necessary for better error handling and code maintenance.
  • Loading branch information
attackant committed Nov 17, 2023
1 parent e617aec commit 01d9e21
Showing 1 changed file with 170 additions and 17 deletions.
187 changes: 170 additions & 17 deletions src/class-omeda-client.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* @package nr
*/

declare(strict_types=1);

namespace WP_Newsletter_Builder;

use WP_Newsletter_Builder\Instance;
Expand All @@ -20,6 +22,20 @@
class Omeda_Client {
use Instance;

/**
* Constant for brand API endpoints
*
* @var string
*/
const BRAND = 'brand';

/**
* Constant for client API endpoints
*
* @var string
*/
const CLIENT = 'client';

/**
* The Omeda API user.
*
Expand All @@ -41,6 +57,13 @@ class Omeda_Client {
*/
private bool $debug = false;

/**
* Whether to use the staging environment for the Omeda API.
*
* @var bool
*/
private bool $use_staging = false;

/**
* The base production URL for the API.
*
Expand Down Expand Up @@ -113,13 +136,6 @@ class Omeda_Client {
*/
private string $client_abbr = '';

/**
* Whether to use the staging URL or not.
*
* @var boolean
*/
private $use_staging = false;

/**
* Initialize and set values for properties.
*/
Expand All @@ -138,6 +154,20 @@ public function __construct( $config ) {
$this->set_mailbox( $config[ 'mailbox' ] );
$this->set_namespace( $config[ 'namespace' ] );
$this->set_reply_to( $config[ 'reply_to' ] );

// Turn on debug mode if we're on a local or staging environment.
if ( 'production' !== ENV ) {
$this->set_debug( true );
$this->set_use_staging( false );
}
// Set staging to use the production endpoint, see NR-5283.
if ( 'staging' === ENV ) {
$this->set_use_staging( false );
}
// Set staging to use the production endpoint, see NR-5283.
if ( 'staging' === ENV ) {
$this->set_use_staging( false );
}
}

/**
Expand Down Expand Up @@ -265,7 +295,7 @@ public function get_endpoint( string $api, string $endpoint ): string {
$url = $this->is_use_staging() ? $this->get_staging_url() : $this->get_base_url();

// Set type based on whether we are calling client or brand APIs.
$type = 'client' === $api ? $this->get_client_abbr() : $this->get_brand();
$type = self::CLIENT === $api ? $this->get_client_abbr() : $this->get_brand();

return "{$url}/webservices/rest/{$api}/{$type}/{$endpoint}/*";
}
Expand Down Expand Up @@ -470,6 +500,27 @@ public function set_from_name( string $from_name ): Omeda_Client {
return $this;
}

/**
* Get the headers for the specified Omeda API service.
*
* Allows other functions to modify headers with
* the nr_modify_omeda_headers filter.
*
* @param string $service The name of the API service.
*
* @return array Returns an array of headers.
*/
private function get_headers( string $service = '' ): array {
$http_response_header = [
'x-omeda-appid' => $this->get_app_id(),
'Content-Type' => 'application/json',
];

$http_response_header = apply_filters( 'nr_modify_omeda_headers', $http_response_header, $service );

return $http_response_header;
}

/**
* Checks if all the required properties are set.
*
Expand Down Expand Up @@ -508,6 +559,8 @@ public function is_email_valid( mixed $email ): WP_Error|string {
/**
* Logs a message (if debug mode is enabled).
*
* Only enabled for local environment.
*
* @param string $message The message to log.
*
* @return void
Expand All @@ -517,7 +570,7 @@ private function log( string $message ): void {
if ( defined( 'DOING_UNIT_TEST' ) && DOING_UNIT_TEST ) {
return;
}
if ( $this->is_debug() ) {
if ( ! ( $this->is_debug() && 'local' === ENV ) ) {
return;
}
error_log( $message ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
Expand All @@ -526,13 +579,13 @@ private function log( string $message ): void {
/**
* Sends an API request to the specified endpoint with the given data.
*
* @param string $service The endpoint to send the request to.
* @param array|string|null $data The data to send with the request.
* @param string $api The API to use (client or brand).
* @param string $service The endpoint to send the request to.
* @param array|null $data The data to send with the request.
* @param string $api The API to use (client or brand).
*
* @return array|WP_Error The response data as an associative array, or a WP_Error object if there was an error.
*/
public function call( string $service, array|string|null $data, string $api = 'client', string $method = 'POST' ): array|WP_Error {
public function call( string $service, ?array $data, string $api = self::CLIENT ): array|WP_Error {
if ( ! $this->check_requirements() ) {
$this->log( 'Missing required properties.' );

Expand All @@ -541,6 +594,12 @@ public function call( string $service, array|string|null $data, string $api = 'c

$endpoint = $this->get_endpoint( $api, $service );

$response = wp_remote_post( esc_url_raw( $endpoint ), [
'headers' => $this->get_headers( $service ),
'body' => wp_json_encode( $data ),
] );

// TODO move content type to get header
if ( 'POST' === $method ) {
$response = wp_remote_post( esc_url_raw( $endpoint ), [
'headers' => [
Expand Down Expand Up @@ -583,6 +642,7 @@ public function call( string $service, array|string|null $data, string $api = 'c
return json_decode( $json, true );
}
return json_decode( $body, true );
//return json_decode( wp_remote_retrieve_body( $response ), true );
}

/**
Expand All @@ -595,7 +655,7 @@ public function call( string $service, array|string|null $data, string $api = 'c
* @return array|WP_Error The response data as an associative array, or a WP_Error object if there was an error.
*/
public function get_deployment_types( ?array $data = null ): WP_Error|array {
return $this->call( 'deploymenttypes', $data, 'brand' );
return $this->call( 'deploymenttypes', $data, self::BRAND );
}

/**
Expand All @@ -614,9 +674,18 @@ public function opt_in( WP_REST_Request $request ): array|WP_Error {
return $email;
}

// NR-5278: Set the input ID to automatically create customers.
$this->set_input_id( '7900G2456689A2G' );
// First, store the customer in Omeda.
$result = $this->store_customer( $email );
if ( is_wp_error( $result ) ) {
return $result;
}

// Check for a 'SubmissionId' in the response.
if ( ! isset( $result['SubmissionId'] ) ) {
return new WP_Error( 'store_customer_error', __( 'Something went wrong while storing the customer.', 'nr' ) );
}

// Then, send the opt-in.
return $this->call( 'optinfilterqueue', [
'DeploymentTypeOptIn' => [
[
Expand Down Expand Up @@ -671,6 +740,90 @@ public function opt_in_out_lookup( string $email ): array|WP_Error {
if ( is_wp_error( $email ) ) {
return $email;
}
return $this->call( 'filter/email/' . $email, null, 'brand' );

return $this->call( 'filter/email/' . $email, null, self::BRAND );
}

/**
* Sends an on-demand deployment (email).
*
* @see https://training.omeda.com/knowledge-base/email-on-demand-send/
*
* @param array $data The data to send to the API.
* @param string|null $email The email address to use. If not provided, it will be extracted from the $data array.
*
* @return array|WP_Error Returns an array of API response data or a WP_Error object if there was an error during API call.
*/
public function send( array $data, ?string $email = null ): array|WP_Error {
// Check for valid email address.
$email = $email ?? $data['EmailAddress'] ?? $data['email'] ?? '';
$email = $this->is_email_valid( $email );
if ( is_wp_error( $email ) ) {
return $email;
}

// The API cannot send email using the Omeda staging URL, so
// for any methods the involve sending email, we need to
// make sure we are using the production URL.
return $this->with_bypass_staging(
fn() => $this->call( 'omail/deployemails', $data, self::BRAND )
);
}

/**
* Execute a callback with the use_staging flag set to false.
*
* Omeda email API calls cannot be made using the staging URL.
*
* @param callable $callback The callback to execute.
*
* @return mixed The result of the callback.
*
* @throws Exception If an exception occurs during the callback execution.
*/
private function with_bypass_staging( callable $callback ): mixed {
// Remember the initial staging status.
$initial_use_staging = $this->is_use_staging();
// Bypass staging.
$this->set_use_staging( false );
try {
// Execute the callback.
$result = $callback();
} finally {
// Ensure use_staging is always reset,
// even if an exception occurs in the callback.
$this->set_use_staging( $initial_use_staging );
}

return $result;
}


/**
* Store a customer by email in the Omeda database.
*
* @param string $email The email address of the customer. Optional, defaults to an empty string.
*
* @return array|WP_Error Returns the response from the API call to store the customer. If the email is invalid, an instance of WP_Error will be returned.
*/
public function store_customer( string $email = '' ): WP_Error|array {
// Check for valid email address.
$email = $this->is_email_valid( $email );
if ( is_wp_error( $email ) ) {
return $email;
}

// This input ID needs to be included when calling
// the Store Customer and Order API.
add_filter('nr_modify_omeda_headers', function ( $headers ) {
$headers['x-omeda-inputid'] = '7900G2456689A2G';
return $headers;
}, 10, 2);

return $this->call( 'storecustomerandorder', [
'Emails' => [
[ 'EmailAddress' => $email ],
],
], self::BRAND );
}
}

0 comments on commit 01d9e21

Please sign in to comment.