Fix pattern route performance (https://github.com/woocommerce/woocommerce-blocks/pull/11535)
* fix pattern route performance * update namespace * improve middleware * improve ProductSchema * improve error handling * update identifier * fix middleware * update description * use schema to return the response * Break down the generate_content method and create the new fetch_dummy_products_to_update method for handling the fetch of dummy products to be updated. * Ensure the Product endpoint relies on the fetch_dummy_products_to_update method for fetching dummy products to avoid code repetition and add safety checks and handle errors in case certain properties are not available. * Add error handling for the Products endpoint. * Remove memory limit increase and update docblocks. * re-add set_time_limit --------- Co-authored-by: Patricia Hillebrandt <patriciahillebrandt@gmail.com>
This commit is contained in:
parent
61b1272d8a
commit
3bde3b174a
|
@ -17,7 +17,7 @@ class ProductUpdater {
|
|||
* @param array $images The array of images.
|
||||
* @param string $business_description The business description.
|
||||
*
|
||||
* @return bool|WP_Error True if the content was generated successfully, WP_Error otherwise.
|
||||
* @return array|WP_Error The generated content for the products. An error if the content could not be generated.
|
||||
*/
|
||||
public function generate_content( $ai_connection, $token, $images, $business_description ) {
|
||||
if ( empty( $business_description ) ) {
|
||||
|
@ -28,16 +28,48 @@ class ProductUpdater {
|
|||
|
||||
if ( $last_business_description === $business_description ) {
|
||||
if ( is_string( $business_description ) && is_string( $last_business_description ) ) {
|
||||
return true;
|
||||
return array(
|
||||
'product_content' => array(),
|
||||
);
|
||||
} else {
|
||||
return new \WP_Error( 'business_description_not_found', __( 'No business description provided for generating AI content.', 'woo-gutenberg-products-block' ) );
|
||||
}
|
||||
}
|
||||
|
||||
$ai_selected_products_images = $this->get_images_information( $images );
|
||||
$products_information_list = $this->assign_ai_selected_images_to_dummy_products_information_list( $ai_selected_products_images );
|
||||
|
||||
$response = $this->generate_product_content( $ai_connection, $token, $products_information_list );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$error_msg = $response;
|
||||
} elseif ( empty( $response ) || ! isset( $response['completion'] ) ) {
|
||||
$error_msg = new \WP_Error( 'missing_completion_key', __( 'The response from the AI service is empty or missing the completion key.', 'woo-gutenberg-products-block' ) );
|
||||
}
|
||||
|
||||
if ( isset( $error_msg ) ) {
|
||||
return $error_msg;
|
||||
}
|
||||
|
||||
$product_content = json_decode( $response['completion'], true );
|
||||
|
||||
return array(
|
||||
'product_content' => $product_content,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all dummy products that were not modified by the store owner.
|
||||
*
|
||||
* @return array|WP_Error An array with the dummy products that need to have their content updated by AI.
|
||||
*/
|
||||
public function fetch_dummy_products_to_update() {
|
||||
$real_products = $this->fetch_product_ids();
|
||||
|
||||
if ( is_array( $real_products ) && count( $real_products ) > 0 ) {
|
||||
return true;
|
||||
return array(
|
||||
'product_content' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
$dummy_products = $this->fetch_product_ids( 'dummy' );
|
||||
|
@ -82,62 +114,7 @@ class ProductUpdater {
|
|||
}
|
||||
}
|
||||
|
||||
if ( empty( $dummy_products_to_update ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$ai_selected_products_images = $this->get_images_information( $images );
|
||||
$products_information_list = $this->assign_ai_selected_images_to_dummy_products_information_list( $ai_selected_products_images );
|
||||
|
||||
$response = $this->generate_product_content( $ai_connection, $token, $products_information_list );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$error_msg = $response;
|
||||
} elseif ( empty( $response ) || ! isset( $response['completion'] ) ) {
|
||||
$error_msg = new \WP_Error( 'missing_completion_key', __( 'The response from the AI service is empty or missing the completion key.', 'woo-gutenberg-products-block' ) );
|
||||
}
|
||||
|
||||
if ( isset( $error_msg ) ) {
|
||||
$this->update_dummy_products( $dummy_products_to_update, $products_information_list );
|
||||
|
||||
return $error_msg;
|
||||
}
|
||||
|
||||
$product_content = json_decode( $response['completion'], true );
|
||||
|
||||
if ( is_null( $product_content ) ) {
|
||||
$this->update_dummy_products( $dummy_products_to_update, $products_information_list );
|
||||
|
||||
return new \WP_Error( 'invalid_json', __( 'The response from the AI service is not a valid JSON.', 'woo-gutenberg-products-block' ) );
|
||||
}
|
||||
|
||||
// This is required to allow the usage of the media_sideload_image function outside the context of /wp-admin/.
|
||||
// See https://developer.wordpress.org/reference/functions/media_sideload_image/ for more details.
|
||||
require_once ABSPATH . 'wp-admin/includes/media.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/image.php';
|
||||
|
||||
$this->update_dummy_products( $dummy_products_to_update, $product_content );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the dummy products with the content from the information list.
|
||||
*
|
||||
* @param array $dummy_products_to_update The dummy products to update.
|
||||
* @param array $products_information_list The products information list.
|
||||
*/
|
||||
public function update_dummy_products( $dummy_products_to_update, $products_information_list ) {
|
||||
$i = 0;
|
||||
foreach ( $dummy_products_to_update as $dummy_product ) {
|
||||
if ( ! isset( $products_information_list[ $i ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->update_product_content( $dummy_product, $products_information_list[ $i ] );
|
||||
++$i;
|
||||
}
|
||||
return $dummy_products_to_update;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -273,11 +250,15 @@ class ProductUpdater {
|
|||
if ( ! isset( $ai_generated_product_content['image']['src'] ) || ! isset( $ai_generated_product_content['image']['alt'] ) || ! isset( $ai_generated_product_content['title'] ) || ! isset( $ai_generated_product_content['description'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/media.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/image.php';
|
||||
|
||||
// Since the media_sideload_image function is expensive and can take longer to complete
|
||||
// the process of downloading the external image and uploading it to the media library,
|
||||
// here we are increasing the time limit and the memory limit to avoid any issues.
|
||||
// here we are increasing the time limit to avoid any issues.
|
||||
set_time_limit( 60 );
|
||||
wp_raise_memory_limit();
|
||||
|
||||
$product_image_id = media_sideload_image( $ai_generated_product_content['image']['src'], $product->get_id(), $ai_generated_product_content['image']['alt'], 'id' );
|
||||
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\StoreApi\Routes\V1\AI;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Patterns\ProductUpdater;
|
||||
use Automattic\WooCommerce\StoreApi\Routes\V1\AbstractRoute;
|
||||
|
||||
/**
|
||||
* BusinessDescription class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class BusinessDescription extends AbstractRoute {
|
||||
/**
|
||||
* The route identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const IDENTIFIER = 'ai/business-description';
|
||||
|
||||
/**
|
||||
* The schema item identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SCHEMA_TYPE = 'ai/business-description';
|
||||
|
||||
/**
|
||||
* Get the path of this REST route.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_path() {
|
||||
return '/ai/business-description';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get method arguments for this REST route.
|
||||
*
|
||||
* @return array An array of endpoints.
|
||||
*/
|
||||
public function get_args() {
|
||||
return [
|
||||
[
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [ $this, 'get_response' ],
|
||||
'permission_callback' => [ Middleware::class, 'is_authorized' ],
|
||||
'args' => [
|
||||
'business_description' => [
|
||||
'description' => __( 'The business description for a given store.', 'woo-gutenberg-products-block' ),
|
||||
'type' => 'string',
|
||||
],
|
||||
],
|
||||
],
|
||||
'schema' => [ $this->schema, 'get_public_item_schema' ],
|
||||
'allow_batch' => [ 'v1' => true ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the last business description.
|
||||
*
|
||||
* @param \WP_REST_Request $request Request object.
|
||||
*
|
||||
* @return bool|string|\WP_Error|\WP_REST_Response
|
||||
*/
|
||||
protected function get_route_post_response( \WP_REST_Request $request ) {
|
||||
|
||||
$business_description = $request->get_param( 'business_description' );
|
||||
|
||||
if ( ! $business_description ) {
|
||||
return $this->error_to_response(
|
||||
new \WP_Error(
|
||||
'invalid_business_description',
|
||||
__( 'Invalid business description.', 'woo-gutenberg-products-block' )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
update_option( 'last_business_description_with_ai_content_generated', $business_description );
|
||||
|
||||
return rest_ensure_response(
|
||||
array(
|
||||
'ai_content_generated' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\StoreApi\Routes\V1\AI;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\AI\Connection;
|
||||
use Automattic\WooCommerce\Blocks\Images\Pexels;
|
||||
use Automattic\WooCommerce\StoreApi\Routes\V1\AbstractRoute;
|
||||
|
||||
/**
|
||||
* Patterns class.
|
||||
*/
|
||||
class Images extends AbstractRoute {
|
||||
/**
|
||||
* The route identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const IDENTIFIER = 'ai/images';
|
||||
|
||||
/**
|
||||
* The schema item identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SCHEMA_TYPE = 'ai/images';
|
||||
|
||||
/**
|
||||
* Get the path of this REST route.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_path() {
|
||||
return '/ai/images';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get method arguments for this REST route.
|
||||
*
|
||||
* @return array An array of endpoints.
|
||||
*/
|
||||
public function get_args() {
|
||||
return [
|
||||
[
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [ $this, 'get_response' ],
|
||||
'permission_callback' => [ Middleware::class, 'is_authorized' ],
|
||||
'args' => [
|
||||
'business_description' => [
|
||||
'description' => __( 'The business description for a given store.', 'woo-gutenberg-products-block' ),
|
||||
'type' => 'string',
|
||||
],
|
||||
],
|
||||
],
|
||||
'schema' => [ $this->schema, 'get_public_item_schema' ],
|
||||
'allow_batch' => [ 'v1' => true ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Images from Pexels
|
||||
*
|
||||
* @param \WP_REST_Request $request Request object.
|
||||
*
|
||||
* @return bool|string|\WP_Error|\WP_REST_Response
|
||||
*/
|
||||
protected function get_route_post_response( \WP_REST_Request $request ) {
|
||||
|
||||
$business_description = sanitize_text_field( wp_unslash( $request['business_description'] ) );
|
||||
|
||||
if ( empty( $business_description ) ) {
|
||||
$business_description = get_option( 'woo_ai_describe_store_description' );
|
||||
}
|
||||
|
||||
$last_business_description = get_option( 'last_business_description_with_ai_content_generated' );
|
||||
|
||||
if ( $last_business_description === $business_description ) {
|
||||
return rest_ensure_response(
|
||||
$this->prepare_item_for_response(
|
||||
[
|
||||
'ai_content_generated' => true,
|
||||
'images' => array(),
|
||||
],
|
||||
$request
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$ai_connection = new Connection();
|
||||
|
||||
$site_id = $ai_connection->get_site_id();
|
||||
|
||||
if ( is_wp_error( $site_id ) ) {
|
||||
return $this->error_to_response( $site_id );
|
||||
}
|
||||
|
||||
$token = $ai_connection->get_jwt_token( $site_id );
|
||||
|
||||
if ( is_wp_error( $token ) ) {
|
||||
return $this->error_to_response( $token );
|
||||
}
|
||||
|
||||
$images = ( new Pexels() )->get_images( $ai_connection, $token, $business_description );
|
||||
|
||||
if ( is_wp_error( $images ) ) {
|
||||
return $this->error_to_response( $images );
|
||||
}
|
||||
|
||||
return rest_ensure_response(
|
||||
$this->prepare_item_for_response(
|
||||
[
|
||||
'ai_content_generated' => true,
|
||||
'images' => $images,
|
||||
],
|
||||
$request
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\StoreApi\Routes\V1\AI;
|
||||
|
||||
use Automattic\WooCommerce\StoreApi\Exceptions\RouteException;
|
||||
|
||||
/**
|
||||
* Middleware class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Middleware {
|
||||
|
||||
|
||||
/**
|
||||
* Ensure that the user is allowed to make this request.
|
||||
*
|
||||
* @throws RouteException If the user is not allowed to make this request.
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_authorized() {
|
||||
try {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
throw new RouteException( 'woocommerce_rest_invalid_user', __( 'You are not allowed to make this request. Please make sure you are logged in.', 'woo-gutenberg-products-block' ), 403 );
|
||||
}
|
||||
} catch ( RouteException $error ) {
|
||||
return new \WP_Error(
|
||||
$error->getErrorCode(),
|
||||
$error->getMessage(),
|
||||
array( 'status' => $error->getCode() )
|
||||
);
|
||||
}
|
||||
|
||||
$allow_ai_connection = get_option( 'woocommerce_blocks_allow_ai_connection' );
|
||||
|
||||
if ( ! $allow_ai_connection ) {
|
||||
try {
|
||||
throw new RouteException( 'ai_connection_not_allowed', __( 'AI content generation is not allowed on this store. Update your store settings if you wish to enable this feature.', 'woo-gutenberg-products-block' ), 403 );
|
||||
} catch ( RouteException $error ) {
|
||||
return new \WP_Error(
|
||||
$error->getErrorCode(),
|
||||
$error->getMessage(),
|
||||
array( 'status' => $error->getCode() )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\StoreApi\Routes\V1\AI;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\AI\Connection;
|
||||
use Automattic\WooCommerce\Blocks\Patterns\PatternUpdater;
|
||||
use Automattic\WooCommerce\StoreApi\Routes\V1\AbstractRoute;
|
||||
|
||||
/**
|
||||
* Patterns class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Patterns extends AbstractRoute {
|
||||
/**
|
||||
* The route identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const IDENTIFIER = 'ai/patterns';
|
||||
|
||||
/**
|
||||
* The schema item identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SCHEMA_TYPE = 'ai/patterns';
|
||||
|
||||
/**
|
||||
* Get the path of this REST route.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_path() {
|
||||
return '/ai/patterns';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get method arguments for this REST route.
|
||||
*
|
||||
* @return array An array of endpoints.
|
||||
*/
|
||||
public function get_args() {
|
||||
return [
|
||||
[
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [ $this, 'get_response' ],
|
||||
'permission_callback' => [ Middleware::class, 'is_authorized' ],
|
||||
'args' => [
|
||||
'business_description' => [
|
||||
'description' => __( 'The business description for a given store.', 'woo-gutenberg-products-block' ),
|
||||
'type' => 'string',
|
||||
],
|
||||
'images' => [
|
||||
'description' => __( 'The images for a given store.', 'woo-gutenberg-products-block' ),
|
||||
'type' => 'object',
|
||||
],
|
||||
],
|
||||
],
|
||||
'schema' => [ $this->schema, 'get_public_item_schema' ],
|
||||
'allow_batch' => [ 'v1' => true ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update patterns with the content and images powered by AI.
|
||||
*
|
||||
* @param \WP_REST_Request $request Request object.
|
||||
*
|
||||
* @return bool|string|\WP_Error|\WP_REST_Response
|
||||
*/
|
||||
protected function get_route_post_response( \WP_REST_Request $request ) {
|
||||
$business_description = sanitize_text_field( wp_unslash( $request['business_description'] ) );
|
||||
|
||||
$ai_connection = new Connection();
|
||||
|
||||
$site_id = $ai_connection->get_site_id();
|
||||
|
||||
if ( is_wp_error( $site_id ) ) {
|
||||
return $this->error_to_response( $site_id );
|
||||
}
|
||||
|
||||
$token = $ai_connection->get_jwt_token( $site_id );
|
||||
|
||||
$images = $request['images'];
|
||||
|
||||
try {
|
||||
( new PatternUpdater() )->generate_content( $ai_connection, $token, $images, $business_description );
|
||||
return rest_ensure_response( array( 'ai_content_generated' => true ) );
|
||||
} catch ( \WP_Error $e ) {
|
||||
return $this->error_to_response( $e );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\StoreApi\Routes\V1\AI;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Patterns\ProductUpdater;
|
||||
use Automattic\WooCommerce\StoreApi\Routes\V1\AbstractRoute;
|
||||
|
||||
/**
|
||||
* Product class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Product extends AbstractRoute {
|
||||
/**
|
||||
* The route identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const IDENTIFIER = 'ai/product';
|
||||
|
||||
/**
|
||||
* The schema item identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SCHEMA_TYPE = 'ai/product';
|
||||
|
||||
/**
|
||||
* Get the path of this REST route.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_path() {
|
||||
return '/ai/product';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get method arguments for this REST route.
|
||||
*
|
||||
* @return array An array of endpoints.
|
||||
*/
|
||||
public function get_args() {
|
||||
return [
|
||||
[
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [ $this, 'get_response' ],
|
||||
'permission_callback' => [ Middleware::class, 'is_authorized' ],
|
||||
'args' => [
|
||||
'index' => [
|
||||
'description' => __( 'The business description for a given store.', 'woo-gutenberg-products-block' ),
|
||||
'type' => 'integer',
|
||||
],
|
||||
'products_information' => [
|
||||
'description' => __( 'Data generated by AI for updating dummy products.', 'woo-gutenberg-products-block' ),
|
||||
'type' => 'object',
|
||||
],
|
||||
],
|
||||
],
|
||||
'schema' => [ $this->schema, 'get_public_item_schema' ],
|
||||
'allow_batch' => [ 'v1' => true ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update product with the content and image powered by AI.
|
||||
*
|
||||
* @param \WP_REST_Request $request Request object.
|
||||
*
|
||||
* @return bool|string|\WP_Error|\WP_REST_Response
|
||||
*/
|
||||
protected function get_route_post_response( \WP_REST_Request $request ) {
|
||||
$product_updater = new ProductUpdater();
|
||||
$dummy_products = $product_updater->fetch_dummy_products_to_update();
|
||||
|
||||
if ( empty( $dummy_products ) ) {
|
||||
return rest_ensure_response(
|
||||
array(
|
||||
'ai_content_generated' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$index = $request['index'];
|
||||
if ( ! is_numeric( $index ) ) {
|
||||
return rest_ensure_response(
|
||||
array(
|
||||
'ai_content_generated' => false,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$products_information = $request['products_information'] ?? array();
|
||||
|
||||
if ( ! isset( $dummy_products[ $index ] ) ) {
|
||||
return rest_ensure_response(
|
||||
array(
|
||||
'ai_content_generated' => false,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$product_updater->update_product_content( $dummy_products[ $index ], $products_information );
|
||||
|
||||
return rest_ensure_response(
|
||||
array(
|
||||
'ai_content_generated' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,30 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce\StoreApi\Routes\V1;
|
||||
namespace Automattic\WooCommerce\StoreApi\Routes\V1\AI;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\AI\Connection;
|
||||
use Automattic\WooCommerce\Blocks\Images\Pexels;
|
||||
use Automattic\WooCommerce\Blocks\Patterns\PatternUpdater;
|
||||
use Automattic\WooCommerce\Blocks\Patterns\ProductUpdater;
|
||||
use Automattic\WooCommerce\StoreApi\Exceptions\RouteException;
|
||||
use Automattic\WooCommerce\StoreApi\Routes\V1\AbstractRoute;
|
||||
|
||||
/**
|
||||
* Patterns class.
|
||||
* Products class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Patterns extends AbstractRoute {
|
||||
class Products extends AbstractRoute {
|
||||
/**
|
||||
* The route identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const IDENTIFIER = 'patterns';
|
||||
const IDENTIFIER = 'ai/products';
|
||||
|
||||
/**
|
||||
* The schema item identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SCHEMA_TYPE = 'patterns';
|
||||
const SCHEMA_TYPE = 'ai/products';
|
||||
|
||||
/**
|
||||
* Get the path of this REST route.
|
||||
|
@ -32,7 +32,7 @@ class Patterns extends AbstractRoute {
|
|||
* @return string
|
||||
*/
|
||||
public function get_path() {
|
||||
return '/patterns';
|
||||
return '/ai/products';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,12 +45,16 @@ class Patterns extends AbstractRoute {
|
|||
[
|
||||
'methods' => \WP_REST_Server::CREATABLE,
|
||||
'callback' => [ $this, 'get_response' ],
|
||||
'permission_callback' => [ $this, 'is_authorized' ],
|
||||
'permission_callback' => [ Middleware::class, 'is_authorized' ],
|
||||
'args' => [
|
||||
'business_description' => [
|
||||
'description' => __( 'The business description for a given store.', 'woo-gutenberg-products-block' ),
|
||||
'type' => 'string',
|
||||
],
|
||||
'images' => [
|
||||
'description' => __( 'The images for a given store.', 'woo-gutenberg-products-block' ),
|
||||
'type' => 'object',
|
||||
],
|
||||
],
|
||||
],
|
||||
'schema' => [ $this->schema, 'get_public_item_schema' ],
|
||||
|
@ -59,30 +63,7 @@ class Patterns extends AbstractRoute {
|
|||
}
|
||||
|
||||
/**
|
||||
* Permission callback.
|
||||
*
|
||||
* @throws RouteException If the user is not allowed to make this request.
|
||||
*
|
||||
* @return true|\WP_Error
|
||||
*/
|
||||
public function is_authorized() {
|
||||
try {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
throw new RouteException( 'woocommerce_rest_invalid_user', __( 'You are not allowed to make this request. Please make sure you are logged in.', 'woo-gutenberg-products-block' ), 403 );
|
||||
}
|
||||
} catch ( RouteException $error ) {
|
||||
return new \WP_Error(
|
||||
$error->getErrorCode(),
|
||||
$error->getMessage(),
|
||||
array( 'status' => $error->getCode() )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the content and images in patterns are powered by AI.
|
||||
* Generate the content for the products.
|
||||
*
|
||||
* @param \WP_REST_Request $request Request object.
|
||||
*
|
||||
|
@ -115,6 +96,7 @@ class Patterns extends AbstractRoute {
|
|||
$this->prepare_item_for_response(
|
||||
[
|
||||
'ai_content_generated' => true,
|
||||
'product_content' => null,
|
||||
],
|
||||
$request
|
||||
)
|
||||
|
@ -126,46 +108,34 @@ class Patterns extends AbstractRoute {
|
|||
$site_id = $ai_connection->get_site_id();
|
||||
|
||||
if ( is_wp_error( $site_id ) ) {
|
||||
return $site_id;
|
||||
return $this->error_to_response( $site_id );
|
||||
}
|
||||
|
||||
$token = $ai_connection->get_jwt_token( $site_id );
|
||||
|
||||
if ( is_wp_error( $token ) ) {
|
||||
return $token;
|
||||
return $this->error_to_response( $token );
|
||||
}
|
||||
|
||||
$images = ( new Pexels() )->get_images( $ai_connection, $token, $business_description );
|
||||
$images = $request['images'];
|
||||
|
||||
if ( is_wp_error( $images ) ) {
|
||||
$response = $this->error_to_response( $images );
|
||||
} else {
|
||||
$populate_patterns = ( new PatternUpdater() )->generate_content( $ai_connection, $token, $images, $business_description );
|
||||
$populate_products = ( new ProductUpdater() )->generate_content( $ai_connection, $token, $images, $business_description );
|
||||
|
||||
if ( is_wp_error( $populate_patterns ) ) {
|
||||
$response = $this->error_to_response( $populate_patterns );
|
||||
}
|
||||
|
||||
$populate_products = ( new ProductUpdater() )->generate_content( $ai_connection, $token, $images, $business_description );
|
||||
|
||||
if ( is_wp_error( $populate_products ) ) {
|
||||
$response = $this->error_to_response( $populate_products );
|
||||
}
|
||||
|
||||
if ( true === $populate_patterns && true === $populate_products ) {
|
||||
update_option( 'last_business_description_with_ai_content_generated', $business_description );
|
||||
}
|
||||
if ( is_wp_error( $populate_products ) ) {
|
||||
return $this->error_to_response( $populate_products );
|
||||
}
|
||||
|
||||
if ( ! isset( $response ) ) {
|
||||
$response = $this->prepare_item_for_response(
|
||||
[
|
||||
'ai_content_generated' => true,
|
||||
],
|
||||
$request
|
||||
);
|
||||
if ( ! isset( $populate_products['product_content'] ) ) {
|
||||
return $this->error_to_response( new \WP_Error( 'product_content_not_found', __( 'Product content not found.', 'woo-gutenberg-products-block' ) ) );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $response );
|
||||
$product_content = $populate_products['product_content'];
|
||||
|
||||
$item = array(
|
||||
'ai_content_generated' => true,
|
||||
'product_content' => $product_content,
|
||||
);
|
||||
|
||||
return rest_ensure_response( $item );
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
<?php
|
||||
namespace Automattic\WooCommerce\StoreApi;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Package;
|
||||
use Automattic\WooCommerce\StoreApi\Routes\V1\AbstractRoute;
|
||||
|
||||
/**
|
||||
|
@ -30,7 +29,7 @@ class RoutesController {
|
|||
public function __construct( SchemaController $schema_controller ) {
|
||||
$this->schema_controller = $schema_controller;
|
||||
$this->routes = [
|
||||
'v1' => [
|
||||
'v1' => [
|
||||
Routes\V1\Batch::IDENTIFIER => Routes\V1\Batch::class,
|
||||
Routes\V1\Cart::IDENTIFIER => Routes\V1\Cart::class,
|
||||
Routes\V1\CartAddItem::IDENTIFIER => Routes\V1\CartAddItem::class,
|
||||
|
@ -48,7 +47,6 @@ class RoutesController {
|
|||
Routes\V1\Checkout::IDENTIFIER => Routes\V1\Checkout::class,
|
||||
Routes\V1\CheckoutOrder::IDENTIFIER => Routes\V1\CheckoutOrder::class,
|
||||
Routes\V1\Order::IDENTIFIER => Routes\V1\Order::class,
|
||||
Routes\V1\Patterns::IDENTIFIER => Routes\V1\Patterns::class,
|
||||
Routes\V1\ProductAttributes::IDENTIFIER => Routes\V1\ProductAttributes::class,
|
||||
Routes\V1\ProductAttributesById::IDENTIFIER => Routes\V1\ProductAttributesById::class,
|
||||
Routes\V1\ProductAttributeTerms::IDENTIFIER => Routes\V1\ProductAttributeTerms::class,
|
||||
|
@ -61,6 +59,14 @@ class RoutesController {
|
|||
Routes\V1\ProductsById::IDENTIFIER => Routes\V1\ProductsById::class,
|
||||
Routes\V1\ProductsBySlug::IDENTIFIER => Routes\V1\ProductsBySlug::class,
|
||||
],
|
||||
// @todo Migrate internal AI routes to WooCommerce Core codebase.
|
||||
'private' => [
|
||||
Routes\V1\AI\Images::IDENTIFIER => Routes\V1\AI\Images::class,
|
||||
Routes\V1\AI\Patterns::IDENTIFIER => Routes\V1\AI\Patterns::class,
|
||||
Routes\V1\AI\Product::IDENTIFIER => Routes\V1\AI\Product::class,
|
||||
Routes\V1\AI\Products::IDENTIFIER => Routes\V1\AI\Products::class,
|
||||
Routes\V1\AI\BusinessDescription::IDENTIFIER => Routes\V1\AI\BusinessDescription::class,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -70,6 +76,7 @@ class RoutesController {
|
|||
public function register_all_routes() {
|
||||
$this->register_routes( 'v1', 'wc/store' );
|
||||
$this->register_routes( 'v1', 'wc/store/v1' );
|
||||
$this->register_routes( 'private', 'wc/private' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -49,12 +49,16 @@ class SchemaController {
|
|||
Schemas\V1\OrderCouponSchema::IDENTIFIER => Schemas\V1\OrderCouponSchema::class,
|
||||
Schemas\V1\OrderFeeSchema::IDENTIFIER => Schemas\V1\OrderFeeSchema::class,
|
||||
Schemas\V1\OrderSchema::IDENTIFIER => Schemas\V1\OrderSchema::class,
|
||||
Schemas\V1\PatternsSchema::IDENTIFIER => Schemas\V1\PatternsSchema::class,
|
||||
Schemas\V1\ProductSchema::IDENTIFIER => Schemas\V1\ProductSchema::class,
|
||||
Schemas\V1\ProductAttributeSchema::IDENTIFIER => Schemas\V1\ProductAttributeSchema::class,
|
||||
Schemas\V1\ProductCategorySchema::IDENTIFIER => Schemas\V1\ProductCategorySchema::class,
|
||||
Schemas\V1\ProductCollectionDataSchema::IDENTIFIER => Schemas\V1\ProductCollectionDataSchema::class,
|
||||
Schemas\V1\ProductReviewSchema::IDENTIFIER => Schemas\V1\ProductReviewSchema::class,
|
||||
Schemas\V1\AI\ImagesSchema::IDENTIFIER => Schemas\V1\AI\ImagesSchema::class,
|
||||
Schemas\V1\AI\PatternsSchema::IDENTIFIER => Schemas\V1\AI\PatternsSchema::class,
|
||||
Schemas\V1\AI\ProductSchema::IDENTIFIER => Schemas\V1\AI\ProductSchema::class,
|
||||
Schemas\V1\AI\ProductsSchema::IDENTIFIER => Schemas\V1\AI\ProductsSchema::class,
|
||||
Schemas\V1\AI\BusinessDescriptionSchema::IDENTIFIER => Schemas\V1\AI\BusinessDescriptionSchema::class,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
namespace Automattic\WooCommerce\StoreApi\Schemas\V1\AI;
|
||||
|
||||
use Automattic\WooCommerce\StoreApi\Schemas\V1\AbstractSchema;
|
||||
|
||||
/**
|
||||
* BusinessDescriptionSchema class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class BusinessDescriptionSchema extends AbstractSchema {
|
||||
/**
|
||||
* The schema item name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title = 'ai/business-description';
|
||||
|
||||
/**
|
||||
* The schema item identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const IDENTIFIER = 'ai/business-description';
|
||||
|
||||
/**
|
||||
* Business Description schema properties.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_properties() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Business Description response.
|
||||
*
|
||||
* @param array $item Item to get response for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_response( $item ) {
|
||||
return [
|
||||
'ai_content_generated' => true,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
namespace Automattic\WooCommerce\StoreApi\Schemas\V1\AI;
|
||||
|
||||
use Automattic\WooCommerce\StoreApi\Schemas\V1\AbstractSchema;
|
||||
|
||||
/**
|
||||
* ImagesSchema class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ImagesSchema extends AbstractSchema {
|
||||
/**
|
||||
* The schema item name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title = 'ai/images';
|
||||
|
||||
/**
|
||||
* The schema item identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const IDENTIFIER = 'ai/images';
|
||||
|
||||
/**
|
||||
* Images schema properties.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_properties() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Images response.
|
||||
*
|
||||
* @param array $item Item to get response for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_response( $item ) {
|
||||
return $item;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,12 @@
|
|||
<?php
|
||||
namespace Automattic\WooCommerce\StoreApi\Schemas\V1;
|
||||
namespace Automattic\WooCommerce\StoreApi\Schemas\V1\AI;
|
||||
|
||||
use Automattic\WooCommerce\StoreApi\Schemas\V1\AbstractSchema;
|
||||
|
||||
/**
|
||||
* OrderSchema class.
|
||||
* PatternsSchema class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class PatternsSchema extends AbstractSchema {
|
||||
/**
|
||||
|
@ -10,14 +14,14 @@ class PatternsSchema extends AbstractSchema {
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title = 'patterns';
|
||||
protected $title = 'ai/patterns';
|
||||
|
||||
/**
|
||||
* The schema item identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const IDENTIFIER = 'patterns';
|
||||
const IDENTIFIER = 'ai/patterns';
|
||||
|
||||
/**
|
||||
* Patterns schema properties.
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
namespace Automattic\WooCommerce\StoreApi\Schemas\V1\AI;
|
||||
|
||||
use Automattic\WooCommerce\StoreApi\Schemas\V1\AbstractSchema;
|
||||
|
||||
/**
|
||||
* ProductSchema class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ProductSchema extends AbstractSchema {
|
||||
/**
|
||||
* The schema item name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title = 'ai/product';
|
||||
|
||||
/**
|
||||
* The schema item identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const IDENTIFIER = 'ai/product';
|
||||
|
||||
/**
|
||||
* Patterns schema properties.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_properties() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Product response.
|
||||
*
|
||||
* @param array $item Item to get response for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_response( $item ) {
|
||||
return [
|
||||
'ai_content_generated' => true,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
namespace Automattic\WooCommerce\StoreApi\Schemas\V1\AI;
|
||||
|
||||
use Automattic\WooCommerce\StoreApi\Schemas\V1\AbstractSchema;
|
||||
|
||||
/**
|
||||
* ProductsSchema class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ProductsSchema extends AbstractSchema {
|
||||
/**
|
||||
* The schema item name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title = 'ai/products';
|
||||
|
||||
/**
|
||||
* The schema item identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const IDENTIFIER = 'ai/products';
|
||||
|
||||
/**
|
||||
* Products schema properties.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_properties() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Products response.
|
||||
*
|
||||
* @param array $item Item to get response for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_item_response( $item ) {
|
||||
return [
|
||||
'ai_content_generated' => $item['ai_content_generated'],
|
||||
'product_content' => $item['product_content'],
|
||||
];
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue