From ff89ce74948d873669c8a50392fe99fc1108a72d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alba=20Rinc=C3=B3n?= Date: Thu, 8 Aug 2024 10:27:29 +0200 Subject: [PATCH] CYS - Move the `ai/store-title` endpoint to woocommerce admin api (#50352) * CYS - Move the ai/store-title endpoint to woocommerce admin api * Add middleware and callback * Add changefile(s) from automation for the following project(s): woocommerce * Fix lint error * Use constant and normalize site title values * Add base AI Endpoint class * Fix lint error * Extract the ai title option name --------- Co-authored-by: github-actions --- .../design-with-ai/services.ts | 2 +- .../changelog/50352-48150-move-endpoints | 4 + .../src/Admin/API/AI/AIEndpoint.php | 55 ++++++ .../src/Admin/API/AI/Middleware.php | 51 ++++++ .../src/Admin/API/AI/StoreTitle.php | 156 +++++++++++++++++ plugins/woocommerce/src/Admin/API/Init.php | 1 + .../src/StoreApi/Routes/V1/AI/StoreTitle.php | 158 ------------------ .../src/StoreApi/RoutesController.php | 13 +- .../src/StoreApi/SchemaController.php | 1 - .../Schemas/V1/AI/StoreTitleSchema.php | 47 ------ 10 files changed, 274 insertions(+), 214 deletions(-) create mode 100644 plugins/woocommerce/changelog/50352-48150-move-endpoints create mode 100644 plugins/woocommerce/src/Admin/API/AI/AIEndpoint.php create mode 100644 plugins/woocommerce/src/Admin/API/AI/Middleware.php create mode 100644 plugins/woocommerce/src/Admin/API/AI/StoreTitle.php delete mode 100644 plugins/woocommerce/src/StoreApi/Routes/V1/AI/StoreTitle.php delete mode 100644 plugins/woocommerce/src/StoreApi/Schemas/V1/AI/StoreTitleSchema.php diff --git a/plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts b/plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts index 5e37c36223f..1f217e1db2d 100644 --- a/plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts +++ b/plugins/woocommerce-admin/client/customize-store/design-with-ai/services.ts @@ -322,7 +322,7 @@ export const updateStorePatterns = async ( }, } ), apiFetch( { - path: '/wc/private/ai/store-title', + path: '/wc-admin/ai/store-title', method: 'POST', data: { business_description: diff --git a/plugins/woocommerce/changelog/50352-48150-move-endpoints b/plugins/woocommerce/changelog/50352-48150-move-endpoints new file mode 100644 index 00000000000..b4078ecd101 --- /dev/null +++ b/plugins/woocommerce/changelog/50352-48150-move-endpoints @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +CYS - Move the ai/store-title endpoint to woocommerce admin API \ No newline at end of file diff --git a/plugins/woocommerce/src/Admin/API/AI/AIEndpoint.php b/plugins/woocommerce/src/Admin/API/AI/AIEndpoint.php new file mode 100644 index 00000000000..ff177b2fa93 --- /dev/null +++ b/plugins/woocommerce/src/Admin/API/AI/AIEndpoint.php @@ -0,0 +1,55 @@ +namespace, + '/' . $this->rest_base . '/' . $this->endpoint, + $args + ); + } + + /** + * Return schema properties. + * + * @return array + */ + abstract public function get_schema(); +} diff --git a/plugins/woocommerce/src/Admin/API/AI/Middleware.php b/plugins/woocommerce/src/Admin/API/AI/Middleware.php new file mode 100644 index 00000000000..10a7b6db94a --- /dev/null +++ b/plugins/woocommerce/src/Admin/API/AI/Middleware.php @@ -0,0 +1,51 @@ +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.', 'woocommerce' ), 403 ); + } catch ( RouteException $error ) { + return new WP_Error( + $error->getErrorCode(), + $error->getMessage(), + array( 'status' => $error->getCode() ) + ); + } + } + + return true; + } +} diff --git a/plugins/woocommerce/src/Admin/API/AI/StoreTitle.php b/plugins/woocommerce/src/Admin/API/AI/StoreTitle.php new file mode 100644 index 00000000000..523b89558e1 --- /dev/null +++ b/plugins/woocommerce/src/Admin/API/AI/StoreTitle.php @@ -0,0 +1,156 @@ +register( + array( + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'update_store_title' ), + 'permission_callback' => array( Middleware::class, 'is_authorized' ), + 'args' => array( + 'business_description' => array( + 'description' => __( 'The business description for a given store.', 'woocommerce' ), + 'type' => 'string', + ), + ), + ), + 'schema' => array( $this, 'get_schema' ), + ) + ); + } + + /** + * Update the store title powered by AI. + * + * @param WP_REST_Request $request Request object. + * + * @return WP_Error|WP_REST_Response + */ + public function update_store_title( $request ) { + + $business_description = $request->get_param( 'business_description' ); + + if ( ! $business_description ) { + return new WP_Error( + 'invalid_business_description', + __( 'Invalid business description.', 'woocommerce' ) + ); + } + + $store_title = html_entity_decode( get_option( self::STORE_TITLE_OPTION_NAME ) ); + $previous_ai_generated_title = html_entity_decode( get_option( self::AI_STORE_TITLE_OPTION_NAME ) ); + + if ( strtolower( trim( self::DEFAULT_TITLE ) ) === strtolower( trim( $store_title ) ) || ( ! empty( $store_title ) && $previous_ai_generated_title !== $store_title ) ) { + return rest_ensure_response( array( 'ai_content_generated' => false ) ); + } + + $ai_generated_title = $this->generate_ai_title( $business_description ); + if ( is_wp_error( $ai_generated_title ) ) { + return $ai_generated_title; + } + + update_option( self::AI_STORE_TITLE_OPTION_NAME, $ai_generated_title ); + update_option( self::STORE_TITLE_OPTION_NAME, $ai_generated_title ); + + return rest_ensure_response( + array( + 'ai_content_generated' => true, + ) + ); + } + + + /** + * Generate the store title powered by AI. + * + * @param string $business_description The business description for a given store. + * + * @return string|WP_Error|WP_REST_Response The store title generated by AI. + */ + private function generate_ai_title( $business_description ) { + $ai_connection = new Connection(); + + $site_id = $ai_connection->get_site_id(); + if ( is_wp_error( $site_id ) ) { + return $site_id; + } + + $token = $ai_connection->get_jwt_token( $site_id ); + if ( is_wp_error( $token ) ) { + return $token; + } + + $prompt = "Generate a store title for a store that has the following: '$business_description'. The length of the title should be 1 and 3 words. The result should include only the store title without any other explanation, number or punctuation marks"; + + $ai_response = $ai_connection->fetch_ai_response( $token, $prompt ); + if ( is_wp_error( $ai_response ) ) { + return $ai_response; + } + + if ( ! isset( $ai_response['completion'] ) ) { + return ''; + } + + return $ai_response['completion']; + } + + /** + * Get the Business Description response. + * + * @return array + */ + public function get_schema() { + return array( + 'ai_content_generated' => true, + ); + } +} diff --git a/plugins/woocommerce/src/Admin/API/Init.php b/plugins/woocommerce/src/Admin/API/Init.php index 3bd3ec3d37b..d8c35352996 100644 --- a/plugins/woocommerce/src/Admin/API/Init.php +++ b/plugins/woocommerce/src/Admin/API/Init.php @@ -86,6 +86,7 @@ class Init { 'Automattic\WooCommerce\Admin\API\NavigationFavorites', 'Automattic\WooCommerce\Admin\API\MobileAppMagicLink', 'Automattic\WooCommerce\Admin\API\ShippingPartnerSuggestions', + 'Automattic\WooCommerce\Admin\API\AI\StoreTitle', ); } diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/StoreTitle.php b/plugins/woocommerce/src/StoreApi/Routes/V1/AI/StoreTitle.php deleted file mode 100644 index 0c345722d92..00000000000 --- a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/StoreTitle.php +++ /dev/null @@ -1,158 +0,0 @@ - \WP_REST_Server::CREATABLE, - 'callback' => array( $this, 'get_response' ), - 'permission_callback' => array( Middleware::class, 'is_authorized' ), - 'args' => array( - 'business_description' => array( - 'description' => __( 'The business description for a given store.', 'woocommerce' ), - 'type' => 'string', - ), - ), - ), - 'schema' => array( $this->schema, 'get_public_item_schema' ), - 'allow_batch' => array( 'v1' => true ), - ); - } - - /** - * Update the store title 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 = $request->get_param( 'business_description' ); - - if ( ! $business_description ) { - return $this->error_to_response( - new \WP_Error( - 'invalid_business_description', - __( 'Invalid business description.', 'woocommerce' ) - ) - ); - } - - $store_title = html_entity_decode( get_option( 'blogname' ) ); - $previous_ai_generated_title = html_entity_decode( get_option( 'ai_generated_site_title' ) ); - - if ( self::DEFAULT_TITLE === $store_title || ( ! empty( $store_title ) && $previous_ai_generated_title !== $store_title ) ) { - return rest_ensure_response( array( 'ai_content_generated' => false ) ); - } - - $ai_generated_title = $this->generate_ai_title( $business_description ); - if ( is_wp_error( $ai_generated_title ) ) { - return $this->error_to_response( $ai_generated_title ); - } - - update_option( 'ai_generated_site_title', $ai_generated_title ); - update_option( self::STORE_TITLE_OPTION_NAME, $ai_generated_title ); - - return rest_ensure_response( - array( - 'ai_content_generated' => true, - ) - ); - } - - /** - * Generate the store title powered by AI. - * - * @param string $business_description The business description for a given store. - * - * @return string|\WP_Error|\WP_REST_Response The store title generated by AI. - */ - private function generate_ai_title( $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 ); - if ( is_wp_error( $token ) ) { - return $this->error_to_response( $token ); - } - - $prompt = "Generate a store title for a store that has the following: '$business_description'. The length of the title should be 1 and 3 words. The result should include only the store title without any other explanation, number or punctuation marks"; - - $ai_response = $ai_connection->fetch_ai_response( $token, $prompt ); - if ( is_wp_error( $ai_response ) ) { - return $this->error_to_response( $ai_response ); - } - - if ( ! isset( $ai_response['completion'] ) ) { - return ''; - } - - return $ai_response['completion']; - } -} diff --git a/plugins/woocommerce/src/StoreApi/RoutesController.php b/plugins/woocommerce/src/StoreApi/RoutesController.php index 64433ce6fe7..986c29e925f 100644 --- a/plugins/woocommerce/src/StoreApi/RoutesController.php +++ b/plugins/woocommerce/src/StoreApi/RoutesController.php @@ -68,14 +68,13 @@ class RoutesController { ], // @todo Migrate internal AI routes to WooCommerce Core codebase. 'private' => [ - Routes\V1\AI\StoreTitle::IDENTIFIER => Routes\V1\AI\StoreTitle::class, - 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\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, - Routes\V1\AI\StoreInfo::IDENTIFIER => Routes\V1\AI\StoreInfo::class, - Routes\V1\Patterns::IDENTIFIER => Routes\V1\Patterns::class, + Routes\V1\AI\StoreInfo::IDENTIFIER => Routes\V1\AI\StoreInfo::class, + Routes\V1\Patterns::IDENTIFIER => Routes\V1\Patterns::class, ], ]; } diff --git a/plugins/woocommerce/src/StoreApi/SchemaController.php b/plugins/woocommerce/src/StoreApi/SchemaController.php index 090d5c9d32d..e0a1cf843cc 100644 --- a/plugins/woocommerce/src/StoreApi/SchemaController.php +++ b/plugins/woocommerce/src/StoreApi/SchemaController.php @@ -54,7 +54,6 @@ class SchemaController { 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\StoreTitleSchema::IDENTIFIER => Schemas\V1\AI\StoreTitleSchema::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, diff --git a/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/StoreTitleSchema.php b/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/StoreTitleSchema.php deleted file mode 100644 index 4310ca5044d..00000000000 --- a/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/StoreTitleSchema.php +++ /dev/null @@ -1,47 +0,0 @@ - true, - ]; - } -}