Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New features #143

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions omnisend/includes/Internal/V1/class-client.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
use Omnisend\SDK\V1\SaveContactResponse;
use Omnisend\SDK\V1\GetContactResponse;
use Omnisend\Internal\ContactFactory;
use Omnisend\SDK\V1\SendBatchResponse;
use Omnisend\SDK\V1\Batch;
use WP_Error;

defined( 'ABSPATH' ) || die( 'no direct access' );
Expand Down Expand Up @@ -296,6 +298,57 @@ public function connect_store( $platform ): ConnectStoreResponse {
return new ConnectStoreResponse( $error );
}

public function send_batch( Batch $batch ): SendBatchResponse {
$error = new WP_Error();
$error->merge_from( $this->check_setup() );

if ( $error->has_errors() ) {
return new SendBatchResponse( $error );
}

$response = wp_remote_post(
OMNISEND_CORE_API_V5 . '/batches',
array(
'body' => wp_json_encode( $batch->to_array() ),
'headers' => array(
'Content-Type' => 'application/json',
'X-API-Key' => $this->api_key,
'X-INTEGRATION-NAME' => $this->plugin_name,
'X-INTEGRATION-VERSION' => $this->plugin_version,
),
'timeout' => 10,
)
);

$http_code = wp_remote_retrieve_response_code( $response );

if ( $http_code >= 400 ) {
$body = wp_remote_retrieve_body( $response );
$err_msg = "HTTP error: {$http_code} - " . wp_remote_retrieve_response_message( $response ) . " - {$body}";
$error->add( 'omnisend_api', $err_msg );

return new SendBatchResponse( $error );
}

$body = wp_remote_retrieve_body( $response );

if ( ! $body ) {
$error->add( 'omnisend_api', 'empty response' );

return new SendBatchResponse( $error );
}

$arr = json_decode( $body, true );

if ( empty( $arr['batchID'] ) ) {
$error->add( 'omnisend_api', 'batchID not found in response.' );

return new SendBatchResponse( $error );
}

return new SendBatchResponse( $error, $arr['batchID'] );
}

/**
* @return WP_Error
*/
Expand Down
207 changes: 207 additions & 0 deletions omnisend/includes/SDK/V1/class-batch.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
<?php
/**
* Omnisend Client
*
* @package OmnisendClient
*/

namespace Omnisend\SDK\V1;

use Omnisend\Internal\Utils;
use WP_Error;

defined( 'ABSPATH' ) || die( 'no direct access' );

/**
* Omnisend Batch class. It should be used with Omnisend Client.
*
*/
class Batch {
public const PRODUCTS_ENDPOINT = 'products';
public const CATEGORIES_ENDPOINT = 'categories';
public const EVENTS_ENDPOINT = 'events';
public const CONTACTS_ENDPOINT = 'contacts';
public const POST_METHOD = 'POST';
public const PUT_METHOD = 'PUT';

private const REQUIRED_PROPERTIES = array(
'endpoint',
'items',
'method',
);
private const STRING_PROPERTIES = array(
'endpoint',
'method',
'origin',
);
private const ARRAY_PROPERTIES = array(
'items',
);
private const AVAILABLE_ENDPOINTS = array(
self::PRODUCTS_ENDPOINT,
self::CATEGORIES_ENDPOINT,
self::EVENTS_ENDPOINT,
self::CONTACTS_ENDPOINT,
);
private const AVAILABLE_METHODS = array(
self::POST_METHOD,
self::PUT_METHOD,
);

/**
* @var string $endpoint
*/
private $endpoint = null;

/**
* @var array $items
*/
private $items = null;

/**
* @var string $method
*/
private $method = null;

/**
* @var string $origin
*/
private $origin = null;

/**
* Validate batch properties.
*
* It ensures that required properties are set and that they are valid.
*
* @return WP_Error
*/
public function validate(): WP_Error {
$error = new WP_Error();
$error = $this->validate_properties( $error );

if ( $error->has_errors() ) {
return $error;
}

$error = $this->validate_values( $error );

return $error;
}

/**
* Convert batch to array
*
* If batch is valid it will be transformed to array that can be sent to Omnisend.
*
* @return array
*/
public function to_array(): array {
if ( $this->validate()->has_errors() ) {
return array();
}

$arr = array(
'endpoint' => $this->endpoint,
'items' => $this->items,
'method' => $this->method,
);

if ( ! empty( $this->origin ) ) {
$arr['origin'] = $this->origin;
}

return $arr;
}

/**
* Sets endpoint
*
* @param string $endpoint
*
* @return void
*/
public function set_endpoint( $endpoint ): void {
$this->endpoint = $endpoint;
}

/**
* Sets batch items
*
* @param array $items
*
* @return void
*/
public function set_items( $items ): void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are you planing to validate items? Because they can be product, category etc and as I understand batch methods will be used to create all of these items.
How would you create a batch of products and send them via this method? Would to_array generate correct request?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Batch should be used with Product/Event/Category class - they return single item and user only needs to save this item into array and set it with set_items e.g.

$data[] = $product->to_array();

$data[] = $category->to_array();

$batch->set_items($data);

or I could implement previously suggested "save/reset", and handle "$data[] = $product->to_array()" for end user. I only need to know when user starts assigning another product values within loop.

Copy link
Collaborator

@nerijuszaniauskas nerijuszaniauskas Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How new developer who first time see our SDK will know following things:

  1. What you can send with batch items?
  2. That you need to pass array of items from objects Product/Event/Category and use function "to_array();" it looks so not intuitive and complicated. I would say event impossible if you do not know Omnisend API. SDK goal is ensure easy usage for anyone who see SDK first time without knowing actual Omnisend API.
  3. How SDK user will know if he set all required properties to Product/Event/Category class objects correctly? You implemented function "validate". It is hard for SDK user and he will not likely call "validate" function on each object and handle errors? So these functions with your suggested usage is useless.

Here is my suggestion:

  1. Instead of "set_items" create function "add_item" that will accept only object of Product/Event/Category class.
  2. In "Batch" function "validate" validate each object using function "validate"
  3. In "Batch" function "to_array" iterate and call each object method "to_array" ("to_array" and "validate" is internal objects functions and SDK user should not care and use them.

With this changes usage is very simple ("to_array" and "validate" used automatically + types defined):

$batch->add_item($product1);
$batch->add_item($product2);
// or 
$batch->add_item($event1);
$batch->add_item($event2);

Also please:

  1. validate if all added items are same type (can not be different)
  2. remove "endpoint" property. This can be automatically calculated by added items
  3. apply same logic for product variant as well

$this->items = $items;
}

/**
* Sets method, it can be "PUT" or "POST"
*
* @param string $method
*
* @return void
*/
public function set_method( $method ): void {
$this->method = $method;
}

/**
* Sets origin of request
*
* @param string $origin
*
* @return void
*/
public function set_origin( $origin ): void {
$this->origin = $origin;
}

/**
* Validates property type
*
* @param WP_Error $error
*
* @return WP_Error $error
*/
private function validate_properties( WP_Error $error ): WP_Error {
foreach ( $this as $property_key => $property_value ) {
if ( in_array( $property_key, self::REQUIRED_PROPERTIES ) && empty( $property_value ) ) {
$error->add( $property, $property_key . ' is a required property.' );
}

if ( $property_value !== null && in_array( $property_key, self::STRING_PROPERTIES ) && ! is_string( $property_value ) ) {
$error->add( $property, $property_key . ' must be a string.' );
}

if ( $property_value !== null && in_array( $property_key, self::ARRAY_PROPERTIES ) && ! is_array( $property_value ) ) {
$error->add( $property, $property_key . ' must be an array.' );
}
}

return $error;
}

/**
* Validates property value
*
* @param WP_Error $error
*
* @return WP_Error $error
*/
private function validate_values( WP_Error $error ): WP_Error {
if ( ! in_array( $this->endpoint, self::AVAILABLE_ENDPOINTS ) ) {
$error->add( 'endpoint', sprintf( 'Endpoint must be one of the following: %s', implode( ', ', self::AVAILABLE_ENDPOINTS ) ) );
}

if ( ! in_array( $this->method, self::AVAILABLE_METHODS ) ) {
$error->add( 'method', sprintf( 'Method must be on of the following: %s', implode( ', ', self::AVAILABLE_METHODS ) ) );
}

if ( empty( $this->items ) || count( $this->items ) > 1000 ) {
$error->add( 'items', sprintf( 'Items are empty or batch size limit: %s was exceeded', 1000 ) );
}

return $error;
}
}
Loading