diff --git a/omnisend/includes/Internal/V1/class-client.php b/omnisend/includes/Internal/V1/class-client.php index 625a4dc..b650225 100644 --- a/omnisend/includes/Internal/V1/class-client.php +++ b/omnisend/includes/Internal/V1/class-client.php @@ -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' ); @@ -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 */ diff --git a/omnisend/includes/SDK/V1/class-batch.php b/omnisend/includes/SDK/V1/class-batch.php new file mode 100644 index 0000000..b21340c --- /dev/null +++ b/omnisend/includes/SDK/V1/class-batch.php @@ -0,0 +1,207 @@ +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 { + $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; + } +} diff --git a/omnisend/includes/SDK/V1/class-category.php b/omnisend/includes/SDK/V1/class-category.php new file mode 100644 index 0000000..7fbb910 --- /dev/null +++ b/omnisend/includes/SDK/V1/class-category.php @@ -0,0 +1,140 @@ +validate_properties( $error ); + + if ( $error->has_errors() ) { + return $error; + } + + $error = $this->validate_values( $error ); + + return $error; + } + + /** + * Convert category to array + * + * If category 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(); + } + + $data = array( + 'categoryId' => $this->category_id, + 'title' => $this->title, + ); + + return $data; + } + + /** + * Sets category id + * + * @param string $category_id + * + * @return void + */ + public function set_category_id( $category_id ): void { + $this->category_id = $category_id; + } + + /** + * Sets category title + * + * @param string $title + * + * @return void + */ + public function set_title( $title ): void { + $this->title = $title; + } + + /** + * 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_key, $property_key . ' is a required property.' ); + } + + if ( $property_value !== null && in_array( $property_key, self::STRING_PROPERTIES ) && ! is_string( $property_value ) ) { + $error->add( $property_key, $property_key . ' must be a string' ); + } + } + + return $error; + } + + /** + * Validates property value + * + * @param WP_Error $error + * + * @return WP_Error $error + */ + private function validate_values( WP_Error $error ): WP_Error { + if ( strlen( $this->category_id ) > 100 ) { + $error->add( 'category_id', 'Category ID must be under 100 characters' ); + } + + if ( strlen( $this->title ) > 100 ) { + $error->add( 'title', 'Title must be under 100 characters' ); + } + + return $error; + } +} diff --git a/omnisend/includes/SDK/V1/class-client.php b/omnisend/includes/SDK/V1/class-client.php index 9572e6c..312cd20 100644 --- a/omnisend/includes/SDK/V1/class-client.php +++ b/omnisend/includes/SDK/V1/class-client.php @@ -60,4 +60,12 @@ public function get_contact_by_email( string $email ): GetContactResponse; * @return ConnectStoreResponse */ public function connect_store( string $platform ): ConnectStoreResponse; + + /** + * Send batch of categories/products/events/contacts + * @param Batch $batch + * + * @return SendBatchResponse + */ + public function send_batch( Batch $batch ): SendBatchResponse; } diff --git a/omnisend/includes/SDK/V1/class-event.php b/omnisend/includes/SDK/V1/class-event.php index ece56bf..ad2d070 100644 --- a/omnisend/includes/SDK/V1/class-event.php +++ b/omnisend/includes/SDK/V1/class-event.php @@ -17,7 +17,6 @@ * */ class Event { - private $contact = null; private $event_name = null; private $event_version = null; @@ -32,8 +31,8 @@ class Event { * @return WP_Error */ public function validate(): WP_Error { - $error = new WP_Error(); + if ( $this->contact instanceof Contact ) { $error->merge_from( $this->contact->validate() ); } @@ -54,12 +53,6 @@ public function validate(): WP_Error { $error->add( 'origin', 'Not a string.' ); } - foreach ( $this->properties as $name ) { - if ( ! is_string( $name ) ) { - $error->add( $name, 'Not a string.' ); - } - } - return $error; } @@ -111,6 +104,17 @@ public function add_properties( $key, $value ): void { $this->properties[ $key ] = $value; } + /** + * Sets properties + * + * @param array $properties + * + * @return void + */ + public function set_properties( $properties ): void { + $this->properties = $properties; + } + /** * Sets contact. * diff --git a/omnisend/includes/SDK/V1/class-product.php b/omnisend/includes/SDK/V1/class-product.php new file mode 100644 index 0000000..9601909 --- /dev/null +++ b/omnisend/includes/SDK/V1/class-product.php @@ -0,0 +1,463 @@ +validate_properties( $error ); + + if ( $error->has_errors() ) { + return $error; + } + + $error = $this->validate_values( $error ); + + return $error; + } + + /** + * Convert product to array + * + * If product 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( + 'currency' => $this->currency, + 'id' => $this->id, + 'status' => $this->status, + 'title' => $this->title, + 'url' => $this->url, + ); + + if ( ! empty( $this->category_ids ) ) { + $arr['categoryIDs'] = $this->category_ids; + } + + if ( ! empty( $this->created_at ) ) { + $arr['createdAt'] = $this->created_at; + } + + if ( ! empty( $this->default_image_url ) ) { + $arr['defaultImageUrl'] = $this->default_image_url; + } + + if ( ! empty( $this->description ) ) { + $arr['description'] = $this->description; + } + + if ( ! empty( $this->images ) ) { + $arr['images'] = $this->images; + } + + if ( ! empty( $this->tags ) ) { + $arr['tags'] = $this->tags; + } + + if ( ! empty( $this->type ) ) { + $arr['type'] = $this->type; + } + + if ( ! empty( $this->updated_at ) ) { + $arr['updatedAt'] = $this->updated_at; + } + + if ( ! empty( $this->vendor ) ) { + $arr['vendor'] = $this->vendor; + } + + if ( ! empty( $this->variants ) ) { + $arr['variants'] = $this->variants; + } + + return $arr; + } + + /** + * Sets category ids + * + * @param array $category_ids + * + * @return void + */ + public function set_category_ids( $category_ids ): void { + $this->category_ids = $category_ids; + } + + /** + * Sets product created date, format: "Y-m-d\Th:i:s\Z" + * + * @param string $created_at + * + * @return void + */ + public function set_created_at( $created_at ): void { + $this->created_at = $created_at; + } + + /** + * Sets currency + * + * @param string $currency + * + * @return void + */ + public function set_currency( $currency ): void { + $this->currency = $currency; + } + + /** + * Sets product default image URL + * + * @param string $default_image_url + * + * @return void + */ + public function set_default_image_url( $default_image_url ): void { + $this->default_image_url = $default_image_url; + } + + /** + * Sets product description + * + * @param string $description + * + * @return void + */ + public function set_descripton( $description ): void { + $this->description = $description; + } + + /** + * Sets product id + * + * @param string $id + * + * @return void + */ + public function set_id( $id ): void { + $this->id = $id; + } + + /** + * Sets product images, should not contain "default image url" + * + * @param array $images + * + * @return void + */ + public function set_images( $images ): void { + $this->images = $images; + } + + /** + * Sets product status + * + * @param string $status + * + * @return void + */ + public function set_status( $status ): void { + $this->status = $status; + } + + /** + * Adds new tag to tag array + * + * @param mixed $tag + * @param bool $clean_up_tag clean up tag to be compatible with Omnisend + * + * @return void + */ + public function add_tag( $tag, $clean_up_tag = true ): void { + if ( $clean_up_tag ) { + $tag = Utils::clean_up_tag( $tag ); + } + + if ( $tag == '' ) { + return; + } + + $this->tags[] = $tag; + } + + /** + * Sets product title + * + * @param string $title + * + * @return void + */ + public function set_title( $title ): void { + $this->title = $title; + } + + /** + * Sets product type + * + * @param string $type + * + * @return void + */ + public function set_type( $type ): void { + $this->type = $type; + } + + /** + * Sets product updated date, format: "Y-m-d\Th:i:s\Z" + * + * @param string $updated_at + * + * @return void + */ + public function set_updated_at( $updated_at ): void { + $this->updated_at = $updated_at; + } + + /** + * Sets product URL + * + * @param string $url + * + * @return void + */ + public function set_url( $url ): void { + $this->url = $url; + } + + /** + * Sets product variants + * + * @param array $variants + * + * @return void + */ + public function set_variants( $variants ): void { + $this->variants = $variants; + } + + /** + * Sets product vendor + * + * @param string $vendor + * + * @return void + */ + public function set_vendor( $vendor ): void { + $this->vendor = $vendor; + } + + /** + * 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_key, $property_key . ' is a required property' ); + } + + if ( $property_value !== null && in_array( $property_key, self::ARRAY_PROPERTIES ) && ! is_array( $property_value ) ) { + $error->add( $property_key, $property_key . ' must be an array' ); + } + + if ( $property_value !== null && in_array( $property_key, self::STRING_PROPERTIES ) && ! is_string( $property_value ) ) { + $error->add( $property_key, $property_key . ' must be a string' ); + } + } + + return $error; + } + + /** + * Validates property value + * + * @param WP_Error $error + * + * @return WP_Error $error + */ + private function validate_values( WP_Error $error ): WP_Error { + foreach ( $this->tags as $tag ) { + if ( ! Utils::is_valid_tag( $tag ) ) { + $error->add( 'tags', 'Tag "' . $tag . '" is not valid. Please cleanup it before setting it.' ); + } + } + + if ( ! in_array( $this->status, self::AVAILABLE_STATUS ) ) { + $error->add( 'status', sprintf( 'Status must be one of the following: %s', implode( ',', self::AVAILABLE_STATUS ) ) ); + } + + if ( strlen( $this->id ) > 100 ) { + $error->add( 'id', 'ID must be under 100 characters' ); + } + + if ( strlen( $this->title ) > 100 ) { + $error->add( 'title', 'Title must be under 100 characters' ); + } + + if ( ! ctype_upper( $this->currency ) ) { + $error->add( 'currency', 'Currency code must be all uppercase' ); + } + + if ( $this->description !== null && strlen( $this->description ) > 300 ) { + $error->add( 'description', 'Description must be under 300 characters' ); + } + + if ( $this->type !== null && strlen( $this->type ) > 100 ) { + $error->add( 'type', 'Type must be under 100 characters' ); + } + + if ( $this->vendor !== null && strlen( $this->vendor ) > 100 ) { + $error->add( 'vendor', 'Vendor must be under 100 characters' ); + } + + if ( $this->default_image_url !== null && ! empty( $this->default_image_url ) && ! filter_var( $this->default_image_url, FILTER_VALIDATE_URL ) ) { + $error->add( 'default_image_url', 'Default image must contain a valid URL' ); + } + + if ( ! filter_var( $this->url, FILTER_VALIDATE_URL ) ) { + $error->add( 'url', 'Url must contain a valid URL' ); + } + + return $error; + } +} diff --git a/omnisend/includes/SDK/V1/class-productvariant.php b/omnisend/includes/SDK/V1/class-productvariant.php new file mode 100644 index 0000000..2febaa1 --- /dev/null +++ b/omnisend/includes/SDK/V1/class-productvariant.php @@ -0,0 +1,335 @@ +validate_properties( $error ); + + if ( $error->has_errors() ) { + return $error; + } + + $error = $this->validate_values( $error ); + + return $error; + } + + /** + * Convert product variants to array + * + * If variants are valid it will be transformed to array that can be sent with Product to Omnisend. + * + * @return array + */ + public function to_array(): array { + if ( $this->validate()->has_errors() ) { + return array(); + } + + $arr = array( + 'id' => $this->id, + 'price' => $this->price, + 'title' => $this->title, + 'url' => $this->url, + ); + + if ( ! empty( $this->default_image_url ) ) { + $arr['default_image_url'] = $this->default_image_url; + } + + if ( ! empty( $this->description ) ) { + $arr['description'] = $this->description; + } + + if ( ! empty( $this->images ) ) { + $arr['images'] = $this->images; + } + + if ( ! empty( $this->sku ) ) { + $arr['sku'] = $this->sku; + } + + if ( ! empty( $this->status ) ) { + $arr['status'] = $this->status; + } + + if ( ! empty( $this->strike_through_price ) ) { + $arr['strikeThroughPrice'] = $this->strike_through_price; + } + + return $arr; + } + + /** + * Sets variant default image URL + * + * @param string $default_image_url + * + * @return void + */ + public function set_default_image_url( $default_image_url ): void { + $this->default_image_url = $default_image_url; + } + + /** + * Sets variant description + * + * @param string $description + * + * @return void + */ + public function set_descripton( $description ): void { + $this->description = $description; + } + + /** + * Sets variant ID + * + * @param string $id + * + * @return void + */ + public function set_id( $id ): void { + $this->id = $id; + } + + /** + * Sets variant images + * + * @param array $images + * + * @return void + */ + public function set_images( $images ): void { + $this->images = $images; + } + + /** + * Sets variant price + * + * @param mixed $price + * + * @return void + */ + public function set_price( $price ): void { + $this->price = $price; + } + + /** + * Sets variant SKU + * + * @param string $sku + * + * @return void + */ + public function set_sku( $sku ): void { + $this->sku = $sku; + } + + /** + * Sets variant status + * + * @param string $status + * + * @return void + */ + public function set_status( $status ): void { + $this->status = $status; + } + + /** + * Sets variant price before discount + * + * @param mixed $strike_through_price + * + * @return void + */ + public function set_strike_through_price( $strike_through_price ): void { + $this->strike_through_price = $strike_through_price; + } + + /** + * Sets variant title + * + * @param string $title + * + * @return void + */ + public function set_title( $title ): void { + $this->title = $title; + } + + /** + * Sets variant URL + * + * @param string $url + * + * @return void + */ + public function set_url( $url ): void { + $this->url = $url; + } + + /** + * 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_key, $property_key . ' is a required property' ); + } + + if ( $property_value !== null && in_array( $property_key, self::NUMERIC_PROPERTIES ) && ! is_numeric( $property_value ) ) { + $error->add( $property_key, $property_key . ' must be a number' ); + } + + if ( $property_value !== null && in_array( $property_key, self::STRING_PROPERTIES ) && ! is_string( $property_value ) ) { + $error->add( $property_key, $property_key . ' must be a string' ); + } + + if ( $property_value !== null && in_array( $property_key, self::ARRAY_PROPERTIES ) && ! is_array( $property_value ) ) { + $error->add( $property_key, $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 ( $this->status !== null && ! in_array( $this->status, Product::AVAILABLE_STATUS ) ) { + $error->add( 'status', sprintf( 'Status must be one of the following: %s', implode( ',', Product::AVAILABLE_STATUS ) ) ); + } + + if ( strlen( $this->id ) > 100 ) { + $error->add( 'id', 'ID must be under 100 characters' ); + } + + if ( strlen( $this->title ) > 100 ) { + $error->add( 'title', 'Title must be under 100 characters' ); + } + + if ( $this->description !== null && strlen( $this->description ) > 300 ) { + $error->add( 'description', 'Description must be under 300 characters' ); + } + + if ( $this->sku !== null && strlen( $this->sku ) > 100 ) { + $error->add( 'default_image_url', 'SKU must be under 100 characters' ); + } + + if ( $this->default_image_url !== null && ! empty( $this->default_image_url ) && ! filter_var( $this->default_image_url, FILTER_VALIDATE_URL ) ) { + $error->add( 'default_image_url', 'Default image URL must contain a valid URL' ); + } + + if ( ! filter_var( $this->url, FILTER_VALIDATE_URL ) ) { + $error->add( 'url', 'Url must contain a valid URL' ); + } + + return $error; + } +} diff --git a/omnisend/includes/SDK/V1/class-sendbatchresponse.php b/omnisend/includes/SDK/V1/class-sendbatchresponse.php new file mode 100644 index 0000000..65a31cc --- /dev/null +++ b/omnisend/includes/SDK/V1/class-sendbatchresponse.php @@ -0,0 +1,40 @@ +wp_error = $wp_error; + $this->batch_id = $batch_id; + } + + /** + * Use to retrieve errors + * + * @return WP_error $wp_error + */ + public function get_wp_error(): WP_Error { + return $this->wp_error; + } + + /** + * Use to retrieve batch ID + * + * @return string $batch_id + */ + public function get_batch_id(): string { + return $this->batch_id; + } +}