From baf1d9beeeeddb1544ed6430c5813238b4628150 Mon Sep 17 00:00:00 2001 From: Dima Date: Sun, 14 May 2023 18:51:12 +0300 Subject: [PATCH 01/36] Integrating Pinterest API V5 with user connection wizard. --- class-pinterest-for-woocommerce.php | 218 +++++++++++++++++++--------- src/API/APIV5.php | 86 ++++++++++- src/API/Advertisers.php | 23 ++- src/API/Auth.php | 1 + src/API/AuthDisconnect.php | 2 +- src/API/Base.php | 17 +-- src/API/Options.php | 7 +- src/API/Tags.php | 26 ++-- 8 files changed, 275 insertions(+), 105 deletions(-) diff --git a/class-pinterest-for-woocommerce.php b/class-pinterest-for-woocommerce.php index 037b1f081..3adf4f323 100644 --- a/class-pinterest-for-woocommerce.php +++ b/class-pinterest-for-woocommerce.php @@ -274,7 +274,9 @@ public function init_plugin() { add_action( 'admin_init', array( Pinterest\AdCredits::class, 'check_if_ads_campaign_is_active' ) ); add_action( 'pinterest_for_woocommerce_token_saved', array( $this, 'set_default_settings' ) ); + add_action( 'pinterest_for_woocommerce_token_saved', array( $this, 'create_commerce_integration' ) ); add_action( 'pinterest_for_woocommerce_token_saved', array( $this, 'update_account_data' ) ); + add_action( 'pinterest_for_woocommerce_token_saved', array( $this, 'update_linked_businesses' ) ); // Handle the Pinterest verification URL. add_action( 'parse_request', array( $this, 'verification_request' ) ); @@ -654,6 +656,15 @@ public static function save_connection_info_data( $connection_info_data ) { return self::save_data( 'connection_info_data', $connection_info_data ); } + /** + * Saves the integration data. + * + * @param $integration_data + * @return bool + */ + public static function save_integration_data( $integration_data ) { + return self::save_data( 'integration_data', $integration_data ); + } /** * Disconnect by clearing the Token and any other data that we should gather from scratch. @@ -858,6 +869,68 @@ public function maybe_inject_verification_code() { } } + /** + * Connects WC to Pinterest. + * + * @since x.x.x + * + * @return array + * @throws PinterestApiException + */ + public static function create_commerce_integration(): array { + $external_business_id = self::generate_external_business_id(); + $connection_data = Pinterest_For_Woocommerce::get_data( 'connection_info_data', true ); + + $response = Pinterest\API\APIV5::make_request( + 'integrations/commerce', + 'POST', + json_encode( + array( + 'external_business_id' => $external_business_id, + 'connected_merchant_id' => $connection_data['merchant_id'] ?? '', + 'connected_advertiser_id' => $connection_data['advertiser_id'] ?? '', + 'connected_tag_id' => $connection_data['tag_id'] ?? '', + ) + ) + ); + + /* + * In case of successful response we save our integration data into a database. + * Data we save includes but not limited to: + * external business id, + * id, + * connected_user_id, + * etc. + */ + Pinterest_For_Woocommerce::save_integration_data( $response ); + return $response; + } + + /** + * Used to generate external business id to pass it Pinterest when creating a connection between WC and Pinterest. + * + * @since x.x.x + * + * @return string + */ + public static function generate_external_business_id(): string { + /** + * Filters the shop's external business id. + * + * This is passed to Pinterest when connecting. + * Should be non-empty and without special characters, + * otherwise the ID will be obtained from the site URL as fallback. + * + * @since x.x.x + * + * @param string $id the shop's external business id. + */ + $id = sanitize_key( (string) apply_filters( 'wc_pinterest_external_business_id', get_bloginfo( 'name' ) ) ); + if ( empty( $id ) ) { + $id = sanitize_key( str_replace( array( 'http', 'https', 'www' ), '', get_bloginfo( 'url' ) ) ); + } + return uniqid( sprintf( '%s-', $id ), false ); + } /** * Fetches the account_data parameters from Pinterest's API @@ -870,49 +943,78 @@ public function maybe_inject_verification_code() { * @throws Exception PHP Exception. */ public static function update_account_data() { - try { + $integration_data = self::get_data( 'integration_data' ); + $account_data = Pinterest\API\APIV5::get_account_info(); + /* + Example of $account_data + array ( + 'monthly_views' => -1, + 'following_count' => 0, + 'account_type' => 'BUSINESS', + 'website_url' => 'http://pinterest.dima.works', + 'username' => 'dmytromaksiuta1', + 'board_count' => 0, + 'pin_count' => -20, + 'profile_image' => 'https://i.pinimg.com/600x600_R/42/f5/36/42f5364f737aff4749a8e9046510828f.jpg', + 'follower_count' => 1, + 'business_name' => 'WooCommerce', + )*/ + + $data = array( + 'username' => $account_data['username'] ?? '', + 'full_name' => '', + 'id' => $integration_data['id'] ?? '', + 'image_medium_url' => $account_data['profile_image'] ?? '', + // Partner is a user who is a business account not a pinner ('BUSINESS', 'PINNER' account types). + 'is_partner' => 'BUSINESS' === ( $account_data['account_type'] ?? '' ), + ); - $account_data = Pinterest\API\Base::get_account_info(); - - if ( 'success' === $account_data['status'] ) { - - $data = array_intersect_key( - (array) $account_data['data'], - array( - 'verified_user_websites' => '', - 'is_any_website_verified' => '', - 'username' => '', - 'full_name' => '', - 'id' => '', - 'image_medium_url' => '', - 'is_partner' => '', - ) - ); - - /* - * For now we assume that the billing is not setup and credits are not redeemed. - * We will be able to check that only when the advertiser will be connected. - * The billing is tied to advertiser. - */ - $data['is_billing_setup'] = false; - $data['coupon_redeem_info'] = array( 'redeem_status' => false ); + $verified_websites = array_reduce( + Pinterest\API\APIV5::get_user_websites()['items'] ?? array(), + function( $carry, $item ) { + if ( 'verified' === $item['status'] ) { + $carry[] = $item['website']; + } + return $carry; + }, + array() + ); - Pinterest_For_Woocommerce()::save_setting( 'account_data', $data ); - return $data; - } + $data += array( + // Array of verified website domain names. + 'verified_user_websites' => $verified_websites, + // Indicates if any of the verified websites is verified true or false. + 'is_any_website_verified' => 0 < count( $verified_websites ), + ); - self::get_linked_businesses( true ); + /* + * For now we assume that the billing is not setup and credits are not redeemed. + * We will be able to check that only when the advertiser will be connected. + * The billing is tied to advertiser. + */ + $data += array( + 'is_billing_setup' => false, + 'coupon_redeem_info' => array( 'redeem_status' => false ), + ); + Pinterest_For_Woocommerce()::save_setting( 'account_data', $data ); + return $data; } catch ( Throwable $th ) { - self::disconnect(); - throw new Exception( esc_html__( 'There was an error getting the account data.', 'pinterest-for-woocommerce' ) ); } + } - return array(); - + /** + * Updates linked businesses. + * + * @since x.x.x + * + * @return void + */ + public static function update_linked_businesses() { + self::get_linked_businesses( true ); } /** @@ -1056,19 +1158,28 @@ public static function check_if_coupon_was_redeemed() { /** * Fetches a fresh copy (if needed or explicitly requested), of the authenticated user's linked business accounts. * - * @param boolean $force_refresh Wether to refresh the data from the API. + * @param bool $force_refresh Whether to refresh the data from the API. * * @return array */ - public static function get_linked_businesses( $force_refresh = false ) { - + public static function get_linked_businesses( bool $force_refresh = false ): array { $linked_businesses = ! $force_refresh ? Pinterest_For_Woocommerce()::get_data( 'linked_businesses' ) : null; - if ( null === $linked_businesses ) { - $linked_businesses = self::update_linked_businesses(); + $account_data = Pinterest_For_Woocommerce()::get_setting( 'account_data' ); + $fetch_linked_businesses = ! empty( $account_data ) && array_key_exists( 'is_partner', $account_data ) && ! $account_data['is_partner']; + + $fetched_businesses = $fetch_linked_businesses ? Pinterest\API\APIV5::get_linked_businesses() : array(); + + if ( ! empty( $fetched_businesses ) && 'success' === $fetched_businesses['status'] ) { + $linked_businesses = $fetched_businesses['data']; + } + + $linked_businesses = $linked_businesses ?? array(); + + self::save_data( 'linked_businesses', $linked_businesses ); } - $linked_businesses = array_map( + /*$linked_businesses = array_map( function ( $business ) { return array( 'value' => $business->id, @@ -1076,34 +1187,7 @@ function ( $business ) { ); }, $linked_businesses - ); - - return $linked_businesses; - } - - - /** - * Grabs a fresh copy of businesses from the API saves & returns them. - * - * @return array - */ - public static function update_linked_businesses() { - - $account_data = Pinterest_For_Woocommerce()::get_setting( 'account_data' ); - $fetch_linked_businesses = - ! empty( $account_data ) && - array_key_exists( 'is_partner', $account_data ) && - ! $account_data['is_partner']; - - $fetched_businesses = $fetch_linked_businesses ? Pinterest\API\Base::get_linked_businesses() : array(); - - if ( ! empty( $fetched_businesses ) && 'success' === $fetched_businesses['status'] ) { - $linked_businesses = $fetched_businesses['data']; - } - - $linked_businesses = $linked_businesses ?? array(); - - self::save_data( 'linked_businesses', $linked_businesses ); + );*/ return $linked_businesses; } diff --git a/src/API/APIV5.php b/src/API/APIV5.php index 548e62dc4..1ae5a8bc4 100644 --- a/src/API/APIV5.php +++ b/src/API/APIV5.php @@ -9,6 +9,8 @@ namespace Automattic\WooCommerce\Pinterest\API; +use Automattic\WooCommerce\Pinterest\PinterestApiException as ApiException; + if ( ! defined( 'ABSPATH' ) ) { exit; } @@ -18,7 +20,7 @@ */ class APIV5 extends Base { - const API_DOMAIN = 'https://api.pinterest.com/v5'; + const API_DOMAIN = 'https://api.pinterest.com/v5'; /** * Prepare request @@ -32,16 +34,86 @@ class APIV5 extends Base { */ public static function prepare_request( $endpoint, $method = 'POST', $payload = array(), $api = '' ) { - $request = array( - 'url' => self::API_DOMAIN . "/{$endpoint}", - 'method' => $method, - 'args' => $payload, - 'headers' => array( + return array( + 'url' => static::API_DOMAIN . "/{$endpoint}", + 'method' => $method, + 'args' => $payload, + 'headers' => array( 'Pinterest-Woocommerce-Version' => PINTEREST_FOR_WOOCOMMERCE_VERSION, + 'Content-Type' => 'application/json', ), + 'data_format' => 'body', ); + } - return $request; + /** + * Returns basic user information. + * + * @since x.x.x + * + * @return array|mixed + * @throws ApiException + */ + public static function get_account_info() { + $integration_data = \Pinterest_For_Woocommerce::get_data( 'integration_data', true ); + return self::make_request( + 'user_account', + 'GET'/*, + json_encode( + array( + 'ad_account_id' => $integration_data['connected_advertiser_id'] ?? '', + ) + )*/ + ); } + + /** + * Returns the list of the user's websites. + * + * @since x.x.x + * + * @return array + * @throws ApiException + */ + public static function get_user_websites() { + return self::make_request( + 'user_account/websites', + 'GET' + ); + } + + /** + * Returns the list of linked businesses. + * + * @since x.x.x + * + * @return array|mixed + * @throws ApiException + */ + public static function get_linked_businesses() { + return self::make_request( 'user_account/businesses', 'GET' ); + } + + /** + * Get the advertiser object from the Pinterest API for the given User ID. + * + * @since x.x.x + * + * @return mixed + */ + public static function get_advertisers( $pinterest_user = null ) { + return self::make_request( 'ad_accounts', 'GET' ); + } + + /** + * Get the advertiser's tracking tags. + * + * @param string $ad_account_id the advertiser_id to request the tags for. + * + * @return mixed + */ + public static function get_advertiser_tags( $ad_account_id ) { + return self::make_request( "ad_accounts/{$ad_account_id}/conversion_tags", 'GET' ); + } } diff --git a/src/API/Advertisers.php b/src/API/Advertisers.php index 05d04782e..358491be1 100644 --- a/src/API/Advertisers.php +++ b/src/API/Advertisers.php @@ -46,31 +46,38 @@ public function get_advertisers( WP_REST_Request $request ) { try { - $advertisers = Base::get_advertisers(); + $advertisers = APIV5::get_advertisers(); $terms_agreed = $request->has_param( 'terms_agreed' ) ? (int) $request->get_param( 'terms_agreed' ) : false; - if ( 'success' !== $advertisers['status'] && 1000 === $advertisers['code'] ) { + /*if ( 'success' !== $advertisers['status'] && 1000 === $advertisers['code'] ) { // User needs to take manual action in Pinterest dashboard. throw new \Exception( esc_html__( 'No advertiser exists.', 'pinterest-for-woocommerce' ), 1000 ); } if ( 'success' !== $advertisers['status'] ) { throw new \Exception( esc_html__( 'Response error', 'pinterest-for-woocommerce' ), 400 ); - } + }*/ - if ( empty( $advertisers['data'] ) && ! empty( $terms_agreed ) ) { + if ( empty( $advertisers['items'] ) && ! empty( $terms_agreed ) ) { $advertiser = Base::create_advertiser( $terms_agreed ); $advertisers['data'] = 'success' === $advertiser['status'] ? array( $advertiser['data'] ) : array(); } - return array( 'advertisers' => $advertisers['data'] ); - + return array( + 'advertisers' => array_map( + function ( $item ) { + return array( + 'id' => $item['id'], + 'name' => $item['name'], + ); + }, + $advertisers['items'] + ), + ); } catch ( \Throwable $th ) { - /* Translators: The error description as returned from the API */ $error_message = sprintf( esc_html__( 'Could not fetch advertisers for Pinterest account ID. [%s]', 'pinterest-for-woocommerce' ), $th->getMessage() ); - return new \WP_Error( \PINTEREST_FOR_WOOCOMMERCE_PREFIX . '_advertisers_error', $error_message, array( 'status' => $th->getCode() ) ); } } diff --git a/src/API/Auth.php b/src/API/Auth.php index 6c914f12c..03eacf2cb 100644 --- a/src/API/Auth.php +++ b/src/API/Auth.php @@ -122,6 +122,7 @@ public function connect_callback( WP_REST_Request $request ) { $this->log_error_and_redirect( $request, $error ); } + wp_safe_redirect( $this->get_redirect_url( $request->get_param( 'view' ) ) ); exit; } diff --git a/src/API/AuthDisconnect.php b/src/API/AuthDisconnect.php index 1eaa97196..0e97562a3 100644 --- a/src/API/AuthDisconnect.php +++ b/src/API/AuthDisconnect.php @@ -8,7 +8,7 @@ namespace Automattic\WooCommerce\Pinterest\API; -use \WP_REST_Request; +use WP_Error; if ( ! defined( 'ABSPATH' ) ) { exit; diff --git a/src/API/Base.php b/src/API/Base.php index db64f591d..4a0db0f72 100644 --- a/src/API/Base.php +++ b/src/API/Base.php @@ -92,8 +92,8 @@ public static function make_request( $endpoint, $method = 'POST', $payload = arr try { - $request = self::prepare_request( $endpoint, $method, $payload, $api ); - $response = self::handle_request( $request ); + $request = static::prepare_request( $endpoint, $method, $payload, $api ); + $response = static::handle_request( $request ); if ( ! empty( $cache_expiry ) ) { $cache_key = self::get_cache_key( $endpoint, $method, $payload, $api ); @@ -163,7 +163,7 @@ public static function prepare_request( $endpoint, $method = 'POST', $payload = $api_version = 'ads/' === $api ? self::API_ADS_VERSION : self::API_VERSION; $request = array( - 'url' => self::API_DOMAIN . "/{$api}v{$api_version}/{$endpoint}", + 'url' => static::API_DOMAIN . "/{$api}v{$api_version}/{$endpoint}", 'method' => $method, 'args' => $payload, 'headers' => array( @@ -248,7 +248,7 @@ public static function invalidate_cached_response( $endpoint, $method, $payload, * @throws Exception PHP exception. * @throws ApiException PHP exception. */ - public static function handle_request( $request ) { + protected static function handle_request( $request ) { $request = wp_parse_args( $request, @@ -260,14 +260,9 @@ public static function handle_request( $request ) { ) ); - $body = ''; - try { - - self::get_token(); - if ( $request['auth_header'] ) { - $request['headers']['Authorization'] = 'Bearer ' . self::$token['access_token']; + $request['headers']['Authorization'] = 'Bearer ' . self::get_token()['access_token']; } $request_args = array( @@ -360,7 +355,7 @@ protected static function parse_response( $response ) { throw new Exception( __( 'Empty body', 'pinterest-for-woocommerce' ), 204 ); } - return (array) json_decode( $response['body'] ); + return json_decode( $response['body'], true ); } diff --git a/src/API/Options.php b/src/API/Options.php index 117f6f99b..415ffea6a 100644 --- a/src/API/Options.php +++ b/src/API/Options.php @@ -44,8 +44,13 @@ public function __construct() { */ public function get_settings() { Pinterest_For_Woocommerce()::maybe_check_billing_setup(); + $settings = Pinterest_For_Woocommerce()::get_settings( true ); + if ( empty( $settings['account_data']['id'] ) ) { + $integration_data = \Pinterest_For_Woocommerce::get_data( 'integration_data' ); + $settings['account_data']['id'] = $integration_data['connected_user_id'] ?? ''; + } return array( - PINTEREST_FOR_WOOCOMMERCE_OPTION_NAME => Pinterest_For_Woocommerce()::get_settings( true ), + PINTEREST_FOR_WOOCOMMERCE_OPTION_NAME => $settings, ); } diff --git a/src/API/Tags.php b/src/API/Tags.php index 3e1ddba34..c6110c408 100644 --- a/src/API/Tags.php +++ b/src/API/Tags.php @@ -45,16 +45,15 @@ public function get_tags( WP_REST_Request $request ) { try { - $tags = array(); - $advertiser_id = $request->get_param( 'advrtsr_id' ); + $ad_account_id = $request->get_param( 'advrtsr_id' ); - if ( ! $advertiser_id ) { + if ( ! $ad_account_id ) { throw new \Exception( esc_html__( 'Advertiser missing', 'pinterest-for-woocommerce' ), 400 ); } - $response = Base::get_advertiser_tags( $advertiser_id ); + $tags = APIV5::get_advertiser_tags( $ad_account_id ); - if ( 'success' !== $response['status'] ) { + /*if ( 'success' !== $response['status'] ) { throw new \Exception( esc_html__( 'Response error', 'pinterest-for-woocommerce' ), 400 ); } @@ -62,17 +61,24 @@ public function get_tags( WP_REST_Request $request ) { if ( empty( $tags ) ) { // No tag created yet. Lets create one. - $tag = Base::create_tag( $advertiser_id ); + $tag = Base::create_tag( $ad_account_id ); if ( 'success' === $tag['status'] ) { $tags[ $tag['data']->id ] = $tag['data']; } else { throw new \Exception( esc_html__( 'Could not create a tag. Please check the logs for additional information.', 'pinterest-for-woocommerce' ), 400 ); } - } - - return $tags; - + }*/ + + return array_map( + function( $tag ) { + return array( + 'id' => $tag['id'], + 'name' => $tag['name'], + ); + }, + $tags['items'] ?? array() + ); } catch ( \Throwable $th ) { /* Translators: The error description as returned from the API */ From 0b70926c12c42674a0f44971f6189a4b3e48e05c Mon Sep 17 00:00:00 2001 From: Dima Date: Fri, 19 May 2023 19:14:52 +0300 Subject: [PATCH 02/36] Finish integration for onboarding process. Refactor React onboarding steps to exclude advertiser and tag selection. --- .../setup-guide/app/steps/ClaimWebsite.js | 74 +- .../source/setup-guide/app/views/WizardApp.js | 12 +- class-pinterest-for-woocommerce.php | 71 +- i18n/languages/pinterest-for-woocommerce.pot | 980 +++++++++++------- package-lock.json | 6 +- src/API/AuthDisconnect.php | 3 +- src/API/Options.php | 1 + 7 files changed, 721 insertions(+), 426 deletions(-) diff --git a/assets/source/setup-guide/app/steps/ClaimWebsite.js b/assets/source/setup-guide/app/steps/ClaimWebsite.js index f1a7ae061..f37f5be71 100644 --- a/assets/source/setup-guide/app/steps/ClaimWebsite.js +++ b/assets/source/setup-guide/app/steps/ClaimWebsite.js @@ -34,6 +34,8 @@ import { useCreateNotice, } from '../helpers/effects'; import documentationLinkProps from '../helpers/documentation-link-props'; +import connectAdvertiser from "../helpers/connect-advertiser"; +import {getNewPath} from "@woocommerce/navigation"; const StaticError = ( { reqError } ) => { if ( reqError?.data?.pinterest_code === undefined ) { @@ -106,6 +108,10 @@ const ClaimWebsite = ( { goToNextStep, view } ) => { const pfwSettings = wcSettings.pinterest_for_woocommerce; useEffect( () => { + // If domain is not verified and verification status is not pending nor success - start verification. + if ( ! Object.values( LABEL_STATUS ).includes( status ) && ! isDomainVerified ) { + handleClaimWebsite(); + } if ( status !== STATUS.PENDING && isDomainVerified ) { setStatus( STATUS.SUCCESS ); } @@ -149,10 +155,7 @@ const ClaimWebsite = ( { goToNextStep, view } ) => { const VerifyButton = () => { const buttonLabels = { - [ STATUS.IDLE ]: __( - 'Start verification', - 'pinterest-for-woocommerce' - ), + [ STATUS.IDLE ]: __( 'Start verification', 'pinterest-for-woocommerce' ), [ STATUS.PENDING ]: __( 'Verifying…', 'pinterest-for-woocommerce' ), [ STATUS.ERROR ]: __( 'Try again', 'pinterest-for-woocommerce' ), [ STATUS.SUCCESS ]: __( 'Verified', 'pinterest-for-woocommerce' ), @@ -160,15 +163,58 @@ const ClaimWebsite = ( { goToNextStep, view } ) => { const text = buttonLabels[ status ]; - if ( Object.values( LABEL_STATUS ).includes( status ) ) { - return ; - } + return ; + }; + + const CompleteSetupButton = () => { + const buttonLabels = { + error: __( 'Try Again', 'pinterest-for-woocommerce' ), + success: __( 'Complete Setup', 'pinterest-for-woocommerce' ), + }; return ( - ); }; + const handleCompleteSetup = async () => { + try { + createNotice( + 'success', + __( + 'Connected successfully.', + 'pinterest-for-woocommerce' + ) + ); + + recordEvent( 'pfw_setup', { + target: 'complete', + trigger: 'setup-complete', + } ); + + // Force reload WC admin page to initiate the relevant dependencies of the Dashboard page. + const path = getNewPath( {}, '/pinterest/settings', {} ); + + window.location = new URL( wcSettings.adminUrl + path ); + } catch ( error ) { + createNotice( + 'error', + __( + 'There was a problem connecting the advertiser.', + 'pinterest-for-woocommerce' + ) + ); + } + }; + return (
{ view === 'wizard' && ( @@ -242,15 +288,9 @@ const ClaimWebsite = ( { goToNextStep, view } ) => { { view === 'wizard' && (
-
) }
diff --git a/assets/source/setup-guide/app/views/WizardApp.js b/assets/source/setup-guide/app/views/WizardApp.js index 24a435d5d..1192d06ba 100644 --- a/assets/source/setup-guide/app/views/WizardApp.js +++ b/assets/source/setup-guide/app/views/WizardApp.js @@ -28,9 +28,8 @@ import { * @param {Object} props React props. * @param {Object} props.query The current query string, parsed into an object, from the page URL. * - * @fires wcadmin_pfw_setup with `{ target: 'setup-account' | 'claim-website' | 'setup-tracking', trigger: 'wizard-stepper' }` when wizard's header step is clicked. + * @fires wcadmin_pfw_setup with `{ target: 'setup-account' | 'claim-website', trigger: 'wizard-stepper' }` when wizard's header step is clicked. * @fires wcadmin_pfw_setup with `{ target: 'claim-website' , trigger: 'setup-account-continue' }` when continue button is clicked. - * @fires wcadmin_pfw_setup with `{ target: 'setup-tracking', trigger: 'claim-website-continue' }` when continue button is clicked. * * @return {JSX.Element} Rendered element. */ @@ -78,15 +77,6 @@ const WizardApp = ( { query } ) => { label: __( 'Claim your website', 'pinterest-for-woocommerce' ), isClickable: isBusinessConnected, }, - { - key: 'setup-tracking', - container: SetupTracking, - label: __( - 'Track conversions with the Pinterest tag', - 'pinterest-for-woocommerce' - ), - isClickable: isBusinessConnected && isDomainVerified, - }, ]; const getSteps = () => { diff --git a/class-pinterest-for-woocommerce.php b/class-pinterest-for-woocommerce.php index 3adf4f323..18d5ac8cb 100644 --- a/class-pinterest-for-woocommerce.php +++ b/class-pinterest-for-woocommerce.php @@ -282,7 +282,7 @@ public function init_plugin() { add_action( 'parse_request', array( $this, 'verification_request' ) ); // Disconnect advertiser if advertiser or tag change. - add_action( 'update_option_pinterest_for_woocommerce', array( $this, 'maybe_disconnect_advertiser' ), 10, 2 ); + //add_action( 'update_option_pinterest_for_woocommerce', array( $this, 'maybe_disconnect_advertiser' ), 10, 2 ); // Init marketing notifications. add_action( Heartbeat::DAILY, array( $this, 'init_marketing_notifications' ) ); @@ -681,60 +681,17 @@ public static function disconnect() { * Just need to clean account data in these cases. */ if ( ! self::is_business_connected() ) { - self::flush_options(); - // At this point we're disconnected. return true; } try { // Disconnect merchant from Pinterest. - $result = Pinterest\API\Base::disconnect_merchant(); - - if ( 'success' !== $result['status'] ) { - throw new \Exception( esc_html__( 'Response error on disconnect merchant.', 'pinterest-for-woocommerce' ), 400 ); - } - - // Disconnect the advertiser from Pinterest. - $connected_advertiser = self::get_setting( 'tracking_advertiser', null ); - $connected_tag = self::get_setting( 'tracking_tag', null ); - - if ( $connected_advertiser && $connected_tag ) { - - try { - - Pinterest\API\AdvertiserConnect::disconnect_advertiser( $connected_advertiser, $connected_tag ); - - } catch ( \Exception $th ) { - - Pinterest\Logger::log( esc_html__( 'There was an error disconnecting the Advertiser.', 'pinterest-for-woocommerce' ) ); - self::flush_options(); - throw new \Exception( esc_html__( 'There was an error disconnecting the Advertiser. Please try again.', 'pinterest-for-woocommerce' ), 400 ); - } - } - + self::delete_commerce_integration(); self::flush_options(); - // At this point we're disconnected. return true; - } catch ( PinterestApiException $e ) { - $code = $e->get_pinterest_code(); - - if ( PinterestApiException::MERCHANT_NOT_FOUND === $code ) { - Pinterest\Logger::log( esc_html__( 'Trying to disconnect while the merchant (id) was not found.', 'pinterest-for-woocommerce' ) ); - - /* - * This is an abnormal state of the application. Caused probably by issues during the connection process. - * It looks like the best course of actions is to flush the options and assume that we are disconnected. - * This way we restore UI connect functionality and allow merchant to retry. - */ - self::flush_options(); - return true; - } - - return false; - } catch ( \Exception $th ) { // There was an error disconnecting merchant. return false; @@ -903,9 +860,33 @@ public static function create_commerce_integration(): array { * etc. */ Pinterest_For_Woocommerce::save_integration_data( $response ); + + self::save_setting( 'tracking_advertiser', $response['connected_advertiser_id'] ); + self::save_setting( 'tracking_tag', $response['connected_tag_id'] ); + return $response; } + /** + * Disconnects WC from Pinterest. + * + * @since x.x.x + * + * @return bool + * @throws PinterestApiException + */ + public static function delete_commerce_integration(): bool { + $external_business_id = Pinterest_For_Woocommerce::get_data( 'integration_data' )['external_business_id']; + + $response = Pinterest\API\APIV5::make_request( + "integrations/commerce/{$external_business_id}", + 'DELETE' + ); + + // @TODO: add proper response handling. Check for success http status 204 or unexpected error in return. + return true; + } + /** * Used to generate external business id to pass it Pinterest when creating a connection between WC and Pinterest. * diff --git a/i18n/languages/pinterest-for-woocommerce.pot b/i18n/languages/pinterest-for-woocommerce.pot index f96d10ce4..a7c4688bc 100644 --- a/i18n/languages/pinterest-for-woocommerce.pot +++ b/i18n/languages/pinterest-for-woocommerce.pot @@ -1,15 +1,15 @@ -# Copyright (C) 2022 WooCommerce +# Copyright (C) 2023 WooCommerce # This file is distributed under the GPL-2.0+. msgid "" msgstr "" -"Project-Id-Version: Pinterest for WooCommerce 1.2.2\n" +"Project-Id-Version: Pinterest for WooCommerce 1.2.19\n" "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/pinterest-for-woocommerce\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2022-10-11T13:35:03+00:00\n" +"POT-Creation-Date: 2023-05-18T16:25:33+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.6.0\n" "X-Domain: pinterest-for-woocommerce\n" @@ -34,119 +34,133 @@ msgstr "" msgid "https://woocommerce.com" msgstr "" -#: class-pinterest-for-woocommerce.php:143 +#: class-pinterest-for-woocommerce.php:153 msgid "Cloning this class is forbidden." msgstr "" -#: class-pinterest-for-woocommerce.php:152 +#: class-pinterest-for-woocommerce.php:162 msgid "Unserializing instances of this class is forbidden." msgstr "" -#: class-pinterest-for-woocommerce.php:160 +#: class-pinterest-for-woocommerce.php:170 msgid "Only a single instance of this class is allowed. Use singleton." msgstr "" #. Translators: The minimum PHP version -#: class-pinterest-for-woocommerce.php:326 +#: class-pinterest-for-woocommerce.php:351 msgid "Pinterest for WooCommerce requires a minimum PHP version of %s." msgstr "" #. Translators: The minimum WP version -#: class-pinterest-for-woocommerce.php:331 +#: class-pinterest-for-woocommerce.php:356 msgid "Pinterest for WooCommerce requires a minimum WordPress version of %s." msgstr "" #. Translators: The minimum WC version -#: class-pinterest-for-woocommerce.php:336 +#: class-pinterest-for-woocommerce.php:361 msgid "Pinterest for WooCommerce requires a minimum WooCommerce version of %s." msgstr "" -#: class-pinterest-for-woocommerce.php:340 +#: class-pinterest-for-woocommerce.php:365 msgid "Pinterest for WooCommerce requires WooCommerce Admin to be enabled." msgstr "" #. Translators: The minimum Action Scheduler version -#: class-pinterest-for-woocommerce.php:345 +#: class-pinterest-for-woocommerce.php:370 msgid "Pinterest for WooCommerce requires a minimum Action Scheduler package of %s. It can be caused by old version of the WooCommerce extensions." msgstr "" #. Translators: The error description -#: class-pinterest-for-woocommerce.php:595 +#: class-pinterest-for-woocommerce.php:623 msgid "Could not decrypt the Pinterest API access token. Try reconnecting to Pinterest. [%s]" msgstr "" -#: class-pinterest-for-woocommerce.php:646 -msgid "Response error on disconnect merchant." -msgstr "" - -#: class-pinterest-for-woocommerce.php:661 -msgid "There was an error disconnecting the Advertiser." -msgstr "" - -#: class-pinterest-for-woocommerce.php:663 -#: class-pinterest-for-woocommerce.php:745 +#: class-pinterest-for-woocommerce.php:753 msgid "There was an error disconnecting the Advertiser. Please try again." msgstr "" -#: class-pinterest-for-woocommerce.php:675 -msgid "Trying to disconnect while the merchant (id) was not found." +#: class-pinterest-for-woocommerce.php:982 +msgid "There was an error getting the account data." msgstr "" -#: class-pinterest-for-woocommerce.php:963 +#: class-pinterest-for-woocommerce.php:1220 msgid "Pinterest for WooCommerce verification page" msgstr "" -#: includes/admin/class-pinterest-for-woocommerce-admin.php:63 -#: includes/admin/class-pinterest-for-woocommerce-admin.php:67 -#: includes/admin/class-pinterest-for-woocommerce-admin.php:94 -#: includes/admin/class-pinterest-for-woocommerce-admin.php:98 -#: includes/admin/class-pinterest-for-woocommerce-admin.php:216 -#: includes/admin/class-pinterest-for-woocommerce-admin.php:223 +#: includes/admin/class-pinterest-for-woocommerce-admin.php:64 +#: includes/admin/class-pinterest-for-woocommerce-admin.php:68 +#: includes/admin/class-pinterest-for-woocommerce-admin.php:95 +#: includes/admin/class-pinterest-for-woocommerce-admin.php:99 +#: includes/admin/class-pinterest-for-woocommerce-admin.php:217 +#: includes/admin/class-pinterest-for-woocommerce-admin.php:224 #: views/attributes/variations-form.php:32 #: assets/source/setup-guide/index.js:52 msgid "Pinterest" msgstr "" -#: includes/admin/class-pinterest-for-woocommerce-admin.php:78 +#: includes/admin/class-pinterest-for-woocommerce-admin.php:79 #: assets/source/components/navigation-classic/main-tab-nav.js:16 msgid "Catalog" msgstr "" -#: includes/admin/class-pinterest-for-woocommerce-admin.php:109 -#: includes/admin/class-pinterest-for-woocommerce-admin.php:157 +#: includes/admin/class-pinterest-for-woocommerce-admin.php:110 +#: includes/admin/class-pinterest-for-woocommerce-admin.php:158 msgid "Setup Pinterest" msgstr "" -#: includes/admin/class-pinterest-for-woocommerce-admin.php:126 +#: includes/admin/class-pinterest-for-woocommerce-admin.php:127 #: assets/source/components/navigation-classic/main-tab-nav.js:26 #: assets/source/setup-guide/index.js:82 msgid "Connection" msgstr "" -#: includes/admin/class-pinterest-for-woocommerce-admin.php:139 +#: includes/admin/class-pinterest-for-woocommerce-admin.php:140 #: assets/source/components/navigation-classic/main-tab-nav.js:21 #: assets/source/setup-guide/index.js:95 msgid "Settings" msgstr "" -#: includes/admin/class-pinterest-for-woocommerce-admin.php:169 +#: includes/admin/class-pinterest-for-woocommerce-admin.php:170 msgid "Landing page" msgstr "" -#: includes/admin/class-pinterest-for-woocommerce-admin.php:492 +#: includes/admin/class-pinterest-for-woocommerce-admin.php:488 msgid "Cheatin' huh?" msgstr "" #. translators: 1: composer command. 2: plugin directory -#: pinterest-for-woocommerce.php:67 -#: pinterest-for-woocommerce.php:85 +#: pinterest-for-woocommerce.php:79 +#: pinterest-for-woocommerce.php:97 msgid "Your installation of the Pinterest for WooCommerce plugin is incomplete. Please run %1$s within the %2$s directory." msgstr "" +#: src/AdCredits.php:98 +#: src/AdCredits.php:237 +#: src/Billing.php:134 +msgid "Advertiser connected but the connection id is missing." +msgstr "" + +#: src/AdCredits.php:112 +msgid "There is no available data for the requested offer code." +msgstr "" + +#. translators: API error message +#: src/AdCredits.php:200 +msgid "Could not fetch ads campaign status due to: %s" +msgstr "" + #: src/Admin/Product/Attributes/AttributesForm.php:128 msgid "Default" msgstr "" +#: src/Admin/Product/Attributes/AttributesTab.php:188 +msgid "Simple product" +msgstr "" + +#: src/Admin/Product/Attributes/AttributesTab.php:189 +msgid "Variable product" +msgstr "" + #: src/Admin/Product/Attributes/Input/ConditionInput.php:27 msgid "Condition" msgstr "" @@ -163,52 +177,59 @@ msgstr "" msgid "Categorization of the product based on the standardized Google Product Taxonomy." msgstr "" -#: src/API/AdvertiserConnect.php:55 +#: src/Admin/Tasks/Onboarding.php:35 +msgid "Get your products in front of engaged shoppers with Pinterest for WooCommerce" +msgstr "" + +#: src/Admin/Tasks/Onboarding.php:53 +msgid "20 minutes" +msgstr "" + +#: src/API/AdvertiserConnect.php:58 msgid "Missing advertiser or tag parameters." msgstr "" #. Translators: The error description as returned from the API -#: src/API/AdvertiserConnect.php:74 +#: src/API/AdvertiserConnect.php:81 msgid "Could not connect advertiser with Pinterest. [%s]" msgstr "" -#: src/API/AdvertiserConnect.php:94 +#: src/API/AdvertiserConnect.php:101 msgid "The advertiser could not be connected to Pinterest." msgstr "" -#: src/API/AdvertiserConnect.php:98 +#: src/API/AdvertiserConnect.php:105 msgid "Incorrect advertiser ID." msgstr "" -#: src/API/AdvertiserConnect.php:131 -#: src/API/AdvertiserConnect.php:137 +#: src/API/AdvertiserConnect.php:154 +#: src/API/AdvertiserConnect.php:168 msgid "The advertiser could not be disconnected from Pinterest." msgstr "" -#: src/API/Advertisers.php:55 -msgid "No advertiser exists." +#. Translators: The error description as returned from the API +#: src/API/Advertisers.php:80 +msgid "Could not fetch advertisers for Pinterest account ID. [%s]" msgstr "" -#: src/API/Advertisers.php:59 -#: src/API/Tags.php:58 -msgid "Response error" +#: src/API/Auth.php:71 +msgid "Something went wrong with your attempt to authorize this App. Please try again." msgstr "" -#. Translators: The error description as returned from the API -#: src/API/Advertisers.php:72 -msgid "Could not fetch advertisers for Pinterest account ID. [%s]" +#: src/API/Auth.php:98 +msgid "Token data missing, please try again later." msgstr "" -#: src/API/Auth.php:74 -msgid "Something went wrong with your attempt to authorize this App. Please try again." +#: src/API/Auth.php:103 +msgid "Connection information missing, please try again later." msgstr "" -#: src/API/Auth.php:97 -msgid "Empty response, please try again later." +#: src/API/Auth.php:121 +msgid "There was an error getting the account data. Please try again later." msgstr "" #. Translators: 1: Request method 2: Request endpoint 3: Response status code 4: Response message 5: Pinterest code -#: src/API/Base.php:131 +#: src/API/Base.php:114 msgid "" "%1$s Request: %2$s\n" "Status Code: %3$s\n" @@ -217,24 +238,24 @@ msgid "" msgstr "" #. Translators: 1: Request method 2: Request endpoint 3: Response status code 4: Response message -#: src/API/Base.php:153 +#: src/API/Base.php:136 msgid "" "%1$s Request: %2$s\n" "Status Code: %3$s\n" "API response: %4$s" msgstr "" -#: src/API/Base.php:243 +#: src/API/Base.php:300 msgid "Reconnect to your Pinterest account" msgstr "" -#: src/API/Base.php:298 +#: src/API/Base.php:355 msgid "Empty body" msgstr "" -#: src/API/Base.php:369 -#: src/API/Base.php:457 -#: src/Merchants.php:133 +#: src/API/Base.php:426 +#: src/API/Base.php:527 +#: src/Merchants.php:139 msgid "Auto-created by Pinterest for WooCommerce" msgstr "" @@ -243,277 +264,300 @@ msgstr "" msgid "Could not fetch linked business accounts for Pinterest account ID. [%s]" msgstr "" -#: src/API/FeedIssues.php:86 +#: src/API/FeedIssues.php:87 msgid "Error downloading feed issues file from Pinterest." msgstr "" #. Translators: The error description as returned from the API -#: src/API/FeedIssues.php:112 +#: src/API/FeedIssues.php:113 msgid "Could not get current feed's issues. [%s]" msgstr "" -#: src/API/FeedIssues.php:130 +#: src/API/FeedIssues.php:131 msgid "Invalid product" msgstr "" -#: src/API/FeedIssues.php:137 +#: src/API/FeedIssues.php:138 msgid "(Variation)" msgstr "" -#: src/API/FeedIssues.php:286 +#: src/API/FeedIssues.php:284 +msgid "No feed ID provided." +msgstr "" + +#: src/API/FeedIssues.php:291 msgid "Could not get feed report from Pinterest." msgstr "" -#: src/API/FeedState.php:126 -#: src/API/FeedState.php:231 +#: src/API/FeedState.php:128 +#: src/API/FeedState.php:233 msgid "XML feed" msgstr "" -#: src/API/FeedState.php:128 +#: src/API/FeedState.php:130 msgid "Product sync is disabled." msgstr "" #. Translators: The error description as returned from the API -#: src/API/FeedState.php:146 +#: src/API/FeedState.php:148 msgid "Error getting feed's state. [%s]" msgstr "" -#: src/API/FeedState.php:170 +#: src/API/FeedState.php:172 msgid "Feed generation in progress." msgstr "" #. translators: 1: Time string, 2: number of products, 3: opening anchor tag, 4: closing anchor tag -#: src/API/FeedState.php:174 +#: src/API/FeedState.php:176 msgid "Last activity: %1$s ago - Wrote %2$s product to %3$sfeed file%4$s." msgid_plural "Last activity: %1$s ago - Wrote %2$s products to %3$sfeed file%4$s." msgstr[0] "" msgstr[1] "" -#: src/API/FeedState.php:190 +#: src/API/FeedState.php:192 msgid "Up to date" msgstr "" #. translators: 1: Time string, 2: total number of products, 3: opening anchor tag, 4: closing anchor tag -#: src/API/FeedState.php:194 +#: src/API/FeedState.php:196 msgid "Successfully generated %1$s ago - Wrote %2$s product to %3$sfeed file%4$s" msgid_plural "Successfully generated %1$s ago - Wrote %2$s products to %3$sfeed file%4$s" msgstr[0] "" msgstr[1] "" -#: src/API/FeedState.php:210 +#: src/API/FeedState.php:212 msgid "Feed generation will start shortly." msgstr "" -#: src/API/FeedState.php:215 +#: src/API/FeedState.php:217 msgid "Feed configuration will start shortly." msgstr "" -#: src/API/FeedState.php:220 -#: src/API/FeedState.php:272 -#: src/Feeds.php:41 -#: src/Feeds.php:86 +#: src/API/FeedState.php:222 +#: src/API/FeedState.php:274 +#: src/Feeds.php:42 +#: src/Feeds.php:84 msgid "Could not get feed info." msgstr "" #. Translators: %1$s Time string, %2$s error message -#: src/API/FeedState.php:223 +#: src/API/FeedState.php:225 msgid "Last activity: %1$s ago - %2$s" msgstr "" -#: src/API/FeedState.php:260 -#: src/API/FeedState.php:309 +#: src/API/FeedState.php:262 +#: src/API/FeedState.php:311 msgid "Product feed not yet configured on Pinterest." msgstr "" -#: src/API/FeedState.php:266 +#: src/API/FeedState.php:268 msgid "Could not get merchant info." msgstr "" -#: src/API/FeedState.php:276 +#: src/API/FeedState.php:278 msgid "Product feed not active." msgstr "" -#: src/API/FeedState.php:284 +#: src/API/FeedState.php:286 msgid "Product feed configured for ingestion on Pinterest" msgstr "" #. Translators: %1$s The URL of the product feed, %2$s Time string -#: src/API/FeedState.php:290 +#: src/API/FeedState.php:292 msgid "Pinterest will fetch your product feed every %2$s" msgstr "" -#: src/API/FeedState.php:301 +#: src/API/FeedState.php:303 msgid "Product feed pending approval on Pinterest." msgstr "" -#: src/API/FeedState.php:305 +#: src/API/FeedState.php:307 msgid "Product feed declined by Pinterest" msgstr "" -#: src/API/FeedState.php:317 +#: src/API/FeedState.php:319 msgid "Remote feed setup" msgstr "" -#: src/API/FeedState.php:358 +#: src/API/FeedState.php:360 msgid "Pinterest tag" msgstr "" -#: src/API/FeedState.php:360 +#: src/API/FeedState.php:362 +#: src/API/FeedState.php:393 msgid "Potential conflicting plugins" msgstr "" -#: src/API/FeedState.php:382 +#: src/API/FeedState.php:391 +msgid "Pinterest Rich Pins" +msgstr "" + +#: src/API/FeedState.php:415 msgid "Feed is not registered with Pinterest." msgstr "" -#: src/API/FeedState.php:394 +#: src/API/FeedState.php:427 msgid "Response error when trying to get feed report from Pinterest." msgstr "" -#: src/API/FeedState.php:398 +#: src/API/FeedState.php:431 msgid "Response error. Feed report contains no feed workflow." msgstr "" -#: src/API/FeedState.php:414 +#: src/API/FeedState.php:447 msgid "Automatically pulled by Pinterest" msgstr "" #. Translators: %1$s Time string, %2$s number of products -#: src/API/FeedState.php:417 -#: src/API/FeedState.php:428 +#: src/API/FeedState.php:450 +#: src/API/FeedState.php:461 msgid "Last pulled: %1$s ago, containing %2$d products" msgstr "" -#: src/API/FeedState.php:425 +#: src/API/FeedState.php:458 msgid "Processing" msgstr "" -#: src/API/FeedState.php:436 +#: src/API/FeedState.php:469 msgid "Feed is under review." msgstr "" -#: src/API/FeedState.php:441 +#: src/API/FeedState.php:474 msgid "The feed is queued for processing." msgstr "" -#: src/API/FeedState.php:447 +#: src/API/FeedState.php:480 msgid "Feed ingestion failed." msgstr "" #. Translators: %1$s Time difference string -#: src/API/FeedState.php:450 +#: src/API/FeedState.php:483 msgid "Last attempt: %1$s ago" msgstr "" -#: src/API/FeedState.php:460 +#: src/API/FeedState.php:493 msgid "Unknown status in workflow." msgstr "" #. Translators: The status text returned by the API. -#: src/API/FeedState.php:463 +#: src/API/FeedState.php:496 msgid "API returned an unknown status: %1$s" msgstr "" -#: src/API/FeedState.php:486 +#: src/API/FeedState.php:519 msgid "Remote sync status" msgstr "" #. Translators: The error message as returned by the Pinterest API -#: src/API/FeedState.php:555 +#: src/API/FeedState.php:588 msgid "Pinterest returned: %1$s" msgstr "" -#: src/API/HealthCheck.php:60 +#: src/API/HealthCheck.php:65 msgid "Could not get approval status from Pinterest." msgstr "" #. Translators: The error description as returned from the API -#: src/API/HealthCheck.php:74 +#: src/API/HealthCheck.php:79 msgid "Could not fetch account status. [%s]" msgstr "" -#: src/API/Options.php:61 +#: src/API/Options.php:67 msgid "Missing option parameters." msgstr "" -#: src/API/Options.php:67 +#: src/API/Options.php:73 msgid "There was an error saving the settings." msgstr "" -#: src/API/Tags.php:52 +#: src/API/Tags.php:51 msgid "Advertiser missing" msgstr "" -#: src/API/Tags.php:70 -msgid "Could not create a tag. Please check the logs for additional information." -msgstr "" - #. Translators: The error description as returned from the API -#: src/API/Tags.php:79 +#: src/API/Tags.php:85 msgid "No tracking tag available. [%s]" msgstr "" +#: src/API/UserInteraction.php:92 +msgid "Unrecognized interaction parameter" +msgstr "" + #: src/Crypto.php:95 #: src/Crypto.php:139 msgid "Could not decrypt key value. Try reconnecting to Pinterest." msgstr "" +#. translators: error message with file path +#: src/FeedFileOperations.php:104 +msgid "Could not open temporary file %s for writing" +msgstr "" + +#. translators: error message with file path +#: src/FeedFileOperations.php:114 +msgid "Temporary file: %s is not writeable." +msgstr "" + +#. translators: 1: temporary file name 2: final file name +#: src/FeedFileOperations.php:135 +msgid "Could not rename %1$s to %2$s" +msgstr "" + #. translators: time in the format hours:minutes:seconds -#: src/FeedGenerator.php:97 +#: src/FeedGenerator.php:119 msgid "Feed scheduled to run at %s." msgstr "" -#: src/FeedGenerator.php:119 +#: src/FeedGenerator.php:141 msgid "Feed generation queued." msgstr "" -#: src/FeedGenerator.php:130 +#: src/FeedGenerator.php:152 msgid "Feed generation start. Preparing temporary files." msgstr "" -#: src/FeedGenerator.php:154 +#: src/FeedGenerator.php:198 msgid "Feed generation end. Moving files to the final destination." msgstr "" -#: src/FeedGenerator.php:165 +#: src/FeedGenerator.php:213 msgid "Feed generated successfully." msgstr "" #. translators: number of products -#: src/FeedGenerator.php:302 +#: src/FeedGenerator.php:301 msgid "Feed batch generated. Wrote %s products to the feed file." msgstr "" -#. translators: error message with file path -#: src/FeedGenerator.php:410 -msgid "Could not open temporary file %s for writing" +#: src/FeedGenerator.php:572 +msgid "Aborting the feed generation after too many retries." msgstr "" -#. translators: error message with file path -#: src/FeedGenerator.php:420 -msgid "Temporary file: %s is not writeable." +#. Translators: The batch number. +#: src/FeedGenerator.php:582 +msgid "There was an error running the batch #%s, it will be rescheduled to run again." msgstr "" -#. translators: 1: temporary file name 2: final file name -#: src/FeedGenerator.php:459 -msgid "Could not rename %1$s to %2$s" +#: src/FeedGenerator.php:659 +msgid "There was not possible to re-schedule the action, no args available." msgstr "" -#: src/FeedRegistration.php:95 +#: src/FeedRegistration.php:96 msgid "Could not register feed." msgstr "" -#: src/Feeds.php:45 -#: src/Feeds.php:90 +#: src/Feeds.php:46 +#: src/Feeds.php:88 msgid "Wrong feed info." msgstr "" -#: src/Feeds.php:57 +#: src/Feeds.php:58 msgid "No feed found with the requested ID." msgstr "" -#: src/Feeds.php:102 -msgid "No feed found with the requested location." +#. translators: %s is the locale code. +#: src/LocaleMapper.php:105 +msgid "No matching Pinterest API locale found for %s" msgstr "" #: src/Merchants.php:71 @@ -525,7 +569,7 @@ msgid "There was an error trying to get the merchant object." msgstr "" #: src/Merchants.php:84 -#: src/Merchants.php:149 +#: src/Merchants.php:172 msgid "Response error when trying to create a merchant or update the existing one." msgstr "" @@ -537,6 +581,10 @@ msgstr "" msgid "No merchant returned in the advertiser's response." msgstr "" +#: src/Merchants.php:155 +msgid "There was a previous error trying to create or update merchant." +msgstr "" + #: src/Notes/Collection/AbstractCompleteOnboarding.php:72 msgid "Complete setup" msgstr "" @@ -581,14 +629,26 @@ msgstr "" msgid "Enable Sync" msgstr "" +#: src/PinterestSyncSettings.php:85 +msgid "Missing method to sync the setting." +msgstr "" + +#: src/PinterestSyncSettings.php:107 +msgid "Tracking advertiser or tag missing" +msgstr "" + +#: src/PinterestSyncSettings.php:113 +msgid "Response error" +msgstr "" + #. translators: plugin version. -#: src/PluginUpdate.php:141 +#: src/PluginUpdate.php:157 msgid "Plugin updated to version: %s." msgstr "" -#. translators: 1: plugin version, 2: error message. -#: src/PluginUpdate.php:163 -msgid "Plugin update to version %1$s error: %2$s" +#. translators: 1: plugin version, 2: failed procedure, 3: error message. +#: src/PluginUpdate.php:180 +msgid "Plugin update to version %1$s. Procedure: %2$s. Error: %3$s" msgstr "" #: src/Product/Attributes/Condition.php:48 @@ -623,6 +683,11 @@ msgstr "" msgid "Visit the settings page to enable it." msgstr "" +#. Translators: 1: Conflicting plugins, 2: Plugins Admin page opening tag, 3: Pinterest settings opening tag, 4: Closing anchor tag +#: src/RichPins.php:293 +msgid "The following installed plugin(s) can potentially cause problems with Rich Pins: %1$s. %2$sRemove conflicting plugins%4$s or %3$smanage Rich Pins settings%4$s." +msgstr "" + #. translators: 1: error message. #: src/Shipping.php:88 msgid "" @@ -631,17 +696,17 @@ msgid "" msgstr "" #. Translators: The error description -#: src/Tracking.php:553 +#: src/Tracking.php:617 msgid "Advertiser connected successfully" msgstr "" #. Translators: The error description -#: src/Tracking.php:558 +#: src/Tracking.php:622 msgid "Could not connect the advertiser. Try to connect from the connection tab. [%s]" msgstr "" #. Translators: 1: Conflicting plugins, 2: Plugins Admin page opening tag, 3: Pinterest settings opening tag, 4: Closing anchor tag -#: src/Tracking.php:620 +#: src/Tracking.php:684 msgid "The following installed plugin(s) can potentially cause problems with tracking: %1$s. %2$sRemove conflicting plugins%4$s or %3$smanage tracking settings%4$s." msgstr "" @@ -657,6 +722,75 @@ msgstr "" msgid "Search for a category…" msgstr "" +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingAdsModal.js:25 +msgid "You are eligible for $125 of Pinterest ad credits. To claim the credits, you would need to add your billing details and spend $15 on Pinterest ads." +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingAdsModal.js:39 +msgid "You are eligible for $125 of Pinterest ad credits. To claim the credits, head over to the Pinterest ads manager and " +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingAdsModal.js:44 +msgid "spend $15 on Pinterest ads." +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingAdsModal.js:86 +msgid "You are one step away from claiming $125 of Pinterest ad credits." +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingAdsModal.js:92 +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingModal.js:43 +msgid "You have successfully set up your Pinterest integration! Your product catalog is being synced and reviewed. This could take up to 2 days." +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingAdsModal.js:99 +msgid "*Ad credits may take up to 24 hours to be credited to account." +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingAdsModal.js:107 +msgid "Got it" +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingAdsModal.js:112 +msgid "Do this later" +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingAdsModal.js:126 +msgid "Add billing details" +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingErrorModal.js:38 +msgid "Advertiser already has a redeemed offer." +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingErrorModal.js:46 +msgid "The merchant already redeemed the offer on another advertiser." +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingErrorModal.js:54 +msgid "Unable to claim Pinterest ads credits as the offer has expired." +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingErrorModal.js:62 +msgid "Unable to claim Pinterest ads credits as the offer code is not available for your country." +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingErrorModal.js:70 +msgid "Offer code can only be redeemed by an advertiser with a credit card billing profile." +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingModal.js:37 +msgid "You have successfully set up your Pinterest integration." +msgstr "" + +#: assets/source/catalog-sync/components/OnboardingModals/OnboardingModal.js:51 +msgid "View catalog" +msgstr "" + +#: assets/source/catalog-sync/sections/AdCreditsNotice.js:89 +msgid "Spend $15 to get $125 in Pinterest ad credits. To claim the credits, add your billing details." +msgstr "" + #: assets/source/catalog-sync/sections/SyncIssuesTable.js:23 msgid "Type" msgstr "" @@ -677,12 +811,17 @@ msgstr "" msgid "Issues" msgstr "" -#: assets/source/catalog-sync/sections/SyncState.js:45 +#. translators: %s credits value with currency formatted using wc_price +#: assets/source/catalog-sync/sections/SyncState.js:49 +msgid "You have %s of free ad credits left to use" +msgstr "" + +#: assets/source/catalog-sync/sections/SyncState.js:60 msgid "Overview" msgstr "" -#: assets/source/catalog-sync/sections/SyncState.js:54 -msgid "Set up and manage ads to increase your reach with Pinterest ads manager" +#: assets/source/catalog-sync/sections/SyncState.js:74 +msgid "Create ads to increase your reach with the Pinterest ads manager" msgstr "" #: assets/source/catalog-sync/sections/SyncStateSummary.js:13 @@ -782,178 +921,307 @@ msgstr "" msgid "Connect your Pinterest Account" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:20 -msgid "Merchant is an affiliate or resale marketplace" +#: assets/source/setup-guide/app/components/HealthCheck/index.js:66 +msgid "Couldn’t retrieve the health status of your account." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:24 -msgid "Merchant does not meet our policy on prohibited products" +#: assets/source/setup-guide/app/components/HealthCheck/index.js:85 +msgid "The feed is being configured. Depending on the number of products this may take a while as the feed needs to be fully generated before it is been sent to Pinterest for registration. You can check the status of the generation process in the Catalog tab." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:28 -msgid "Merchant offers services rather than products" +#: assets/source/setup-guide/app/components/HealthCheck/index.js:93 +msgid "Please hold on tight as your account is pending approval from Pinterest. This may take up to 5 business days." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:32 -msgid "Merchant's domain age does not meet minimum requirement" +#: assets/source/setup-guide/app/components/HealthCheck/index.js:101 +#: assets/source/setup-guide/app/components/HealthCheck/index.js:128 +msgid "Your merchant account is disapproved." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:36 -msgid "Merchant domain mismatched with merchant account" +#: assets/source/setup-guide/app/components/HealthCheck/index.js:105 +msgid "If you have a valid reason for appealing a merchant review decision (such as having corrected the violations that resulted in the disapproval), you can submit an appeal." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:40 -msgid "Merchant's URL is broken or requires registration" +#: assets/source/setup-guide/app/components/HealthCheck/index.js:113 +msgid "Submit an appeal" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:44 -msgid "Merchant's URL is incomplete or inaccessible" +#: assets/source/setup-guide/app/components/HealthCheck/index.js:132 +msgid "Please hold on tight as there is an Appeal pending for your Pinterest account." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:48 -msgid "Merchant's shipping policy is unclear or unavailable" +#: assets/source/setup-guide/app/components/HealthCheck/index.js:140 +msgid "Unable to upload catalog." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:52 -msgid "Merchant's returns policy is unclear or unavailable" +#: assets/source/setup-guide/app/components/HealthCheck/index.js:144 +msgid "It looks like your Pinterest business account is connected to another e-commerce platform. Only one platform can be linked to a business account. To upload your catalog, disconnect your business account from the other platform and try again." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:56 -msgid "Merchant's information is incomplete or plagiarized" +#: assets/source/setup-guide/app/components/HealthCheck/index.js:152 +msgid "Unable to register feed." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:60 -msgid "Merchant's products are out of stock" +#: assets/source/setup-guide/app/components/HealthCheck/index.js:156 +msgid "It looks like your WordPress language settings are not supported by Pinterest." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:64 -msgid "Merchant's website includes banner or pop-up ads" +#: assets/source/setup-guide/app/components/HealthCheck/index.js:172 +msgid "Could not fetch account status." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:68 -msgid "Merchant's products do not meet image quality requirements" +#: assets/source/setup-guide/app/components/SaveSettingsButton.js:75 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:202 +#: assets/source/setup-guide/app/steps/SetupTracking.js:301 +msgid "The advertiser was connected successfully." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:72 -msgid "Merchant's product images include watermarks" +#: assets/source/setup-guide/app/components/SaveSettingsButton.js:93 +msgid "Your settings have been saved successfully." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:76 -msgid "Merchant's products are always on sale" +#: assets/source/setup-guide/app/components/SaveSettingsButton.js:101 +#: assets/source/setup-guide/app/steps/AdvancedSettings.js:36 +#: assets/source/setup-guide/app/steps/SetupPins.js:60 +#: assets/source/setup-guide/app/steps/SetupProductSync.js:36 +#: assets/source/setup-guide/app/steps/SetupTracking.js:236 +msgid "There was a problem saving your settings." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:80 -msgid "Merchant's products refer to outdated content" +#: assets/source/setup-guide/app/components/SaveSettingsButton.js:113 +msgid "Saving settings…" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:84 -msgid "Merchant's website uses generic product descriptions" +#: assets/source/setup-guide/app/components/SaveSettingsButton.js:114 +msgid "Save changes" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:88 -msgid "Merchant's website displays several pop-up messages" +#: assets/source/setup-guide/app/components/SyncSettings/index.js:43 +msgid "Settings successfully synced with Pinterest Ads Manager." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:92 -msgid "Merchant's product images are unavailable or mismatched" +#: assets/source/setup-guide/app/components/SyncSettings/index.js:54 +msgid "Failed to sync settings with Pinterest Ads Manager." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:96 -msgid "Resale marketplaces are not allowed" +#: assets/source/setup-guide/app/components/SyncSettings/index.js:92 +msgid "Syncing settings" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:100 -msgid "Affiliate links are not allowed" +#: assets/source/setup-guide/app/components/SyncSettings/index.js:100 +msgid "Sync to get latest settings from Pinterest Ads Manager" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:104 -msgid "Account does not meet the website requirements for verification" +#: assets/source/setup-guide/app/components/SyncSettings/index.js:107 +msgid "Sync" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:108 -msgid "Account does not meet the product requirements for verification" +#: assets/source/setup-guide/app/components/TermsAndConditionsModal.js:37 +msgid "Pinterest Terms & Conditions" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:112 -msgid "Account does not meet the brand reputation criteria for verification" +#: assets/source/setup-guide/app/components/TermsAndConditionsModal.js:47 +msgid "To be eligible and redeem the $125 ad credit from Pinterest, you must complete the setup of Pinterest for WooCommerce, set up your billing with Pinterest Ads manager, and spend $15 with Pinterest ads. Credits may take up to 24 hours to be credited to the user." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:150 -msgid "Couldn’t retrieve the health status of your account." +#: assets/source/setup-guide/app/components/TermsAndConditionsModal.js:53 +msgid "Each user is only eligible to receive the credits once. Ad credits may vary by country and is subject to availability." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:169 -msgid "The feed is being configured. Depending on the number of products this may take a while as the feed needs to be fully generated before it is been sent to Pinterest for registration. You can check the status of the generation process in the Catalog tab." +#: assets/source/setup-guide/app/components/TermsAndConditionsModal.js:59 +msgid "The following terms and conditions apply:" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:177 -msgid "Please hold on tight as your account is pending approval from Pinterest. This may take up to 5 business days." +#: assets/source/setup-guide/app/components/TermsAndConditionsModal.js:65 +msgid "Business Terms of Service" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:185 -#: assets/source/setup-guide/app/components/HealthCheck/index.js:212 -msgid "Your merchant account is disapproved." +#: assets/source/setup-guide/app/components/TermsAndConditionsModal.js:83 +msgid "Privacy Policy" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:189 -msgid "If you have a valid reason for appealing a merchant review decision (such as having corrected the violations that resulted in the disapproval), you can submit an appeal." +#: assets/source/setup-guide/app/components/TermsAndConditionsModal.js:101 +msgid "Pinterest Advertising Services Agreement" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:197 -msgid "Submit an appeal" +#: assets/source/setup-guide/app/components/ThirdPartyTagsNotice.js:12 +msgid "Potential conflicting plugins detected." msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:216 -msgid "Please hold on tight as there is an Appeal pending for your Pinterest account." +#: assets/source/setup-guide/app/components/TopBar.js:15 +msgid "Get started with Pinterest for WooCommerce" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:224 -msgid "Unable to upload catalog." +#: assets/source/setup-guide/app/components/UnsupportedCountryNotice/index.js:45 +msgid "Your store’s country is . This country is currently not supported by Pinterest for WooCommerce. However, you can still choose to list your products in supported countries, if you are able to sell your products to customers there. Change your store’s country here. Read more about supported countries" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:228 -msgid "It looks like your Pinterest business account is connected to another e-commerce platform. Only one platform can be linked to a business account. To upload your catalog, disconnect your business account from the other platform and try again." +#: assets/source/setup-guide/app/constants.js:39 +msgid "Merchant is an affiliate or resale marketplace" msgstr "" -#: assets/source/setup-guide/app/components/HealthCheck/index.js:244 -msgid "Could not fetch account status." +#: assets/source/setup-guide/app/constants.js:43 +msgid "Merchant is a resale marketplace" msgstr "" -#: assets/source/setup-guide/app/components/SaveSettingsButton.js:70 -#: assets/source/setup-guide/app/steps/SetupTracking.js:298 -msgid "The advertiser was connected successfully." +#: assets/source/setup-guide/app/constants.js:47 +msgid "Merchant is an affiliate marketplace or marketer" msgstr "" -#: assets/source/setup-guide/app/components/SaveSettingsButton.js:80 -msgid "Your settings have been saved successfully." +#: assets/source/setup-guide/app/constants.js:51 +msgid "Merchant is a wholesale seller" msgstr "" -#: assets/source/setup-guide/app/components/SaveSettingsButton.js:88 -#: assets/source/setup-guide/app/steps/AdvancedSettings.js:36 -#: assets/source/setup-guide/app/steps/SetupPins.js:47 -#: assets/source/setup-guide/app/steps/SetupProductSync.js:36 -#: assets/source/setup-guide/app/steps/SetupTracking.js:234 -msgid "There was a problem saving your settings." +#: assets/source/setup-guide/app/constants.js:55 +msgid "Merchant does not meet our policy on prohibited products" msgstr "" -#: assets/source/setup-guide/app/components/SaveSettingsButton.js:100 -msgid "Saving settings…" +#: assets/source/setup-guide/app/constants.js:59 +msgid "Merchant offers services rather than products" msgstr "" -#: assets/source/setup-guide/app/components/SaveSettingsButton.js:101 -msgid "Save changes" +#: assets/source/setup-guide/app/constants.js:63 +msgid "Merchant's domain age does not meet minimum requirement" msgstr "" -#: assets/source/setup-guide/app/components/ThirdPartyTagsNotice.js:12 -msgid "Potential conflicting plugins detected." +#: assets/source/setup-guide/app/constants.js:67 +msgid "Merchant domain mismatched with merchant account" msgstr "" -#: assets/source/setup-guide/app/components/TopBar.js:15 -msgid "Get started with Pinterest for WooCommerce" +#: assets/source/setup-guide/app/constants.js:71 +#: assets/source/setup-guide/app/constants.js:75 +msgid "Merchant's shipping policy is unclear or unavailable" msgstr "" -#: assets/source/setup-guide/app/components/UnsupportedCountryNotice/index.js:45 -msgid "Your store’s country is . This country is currently not supported by Pinterest for WooCommerce. However, you can still choose to list your products in supported countries, if you are able to sell your products to customers there. Change your store’s country here. Read more about supported countries" +#: assets/source/setup-guide/app/constants.js:79 +#: assets/source/setup-guide/app/constants.js:83 +msgid "Merchant's returns policy is unclear or unavailable" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:87 +msgid "Merchant's URL is broken or requires registration" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:91 +msgid "Merchant's domain URL is broken" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:95 +msgid "Merchant's domain requires registration to view products" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:99 +msgid "Merchant's URL is incomplete" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:103 +msgid "Merchant's domain does not meet brand information requirements" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:107 +msgid "There is no 'About Us' page or no social information in your website" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:111 +msgid "There is no contact information in your website" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:115 +msgid "Merchant's products are out of stock" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:119 +msgid "Merchant's website includes banner or pop-up ads" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:123 +#: assets/source/setup-guide/app/constants.js:127 +msgid "Merchant's products do not meet image quality requirements" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:131 +msgid "Merchant's product images include watermarks" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:135 +msgid "Merchant's products are always on sale" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:139 +msgid "Merchant's products refer to outdated content" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:143 +msgid "Merchant's website uses generic product descriptions" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:147 +msgid "Merchant's product descriptions and categories do not meet requirements" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:151 +msgid "Merchant's website displays several pop-up messages" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:155 +#: assets/source/setup-guide/app/constants.js:233 +msgid "Merchant does not meet minimum website quality requirements" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:159 +msgid "Merchant's social media links are broken" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:163 +msgid "We're unable to include you as a merchant at this time. We determined your account doesn't comply with our guidelines. Your full catalog has been removed from Pinterest." +msgstr "" + +#: assets/source/setup-guide/app/constants.js:167 +msgid "Merchant's product images are unavailable or mismatched" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:172 +msgid "We recently updated our merchant guidelines and have found that your account is currently not in compliance with our guidelines. Merchants who do not comply with our guidelines will not be able to distribute or promote product Pins from their catalog on Pinterest. If you’d like to appeal this decision, review our guidelines for more detailed information on how you can get your products on Pinterest." +msgstr "" + +#: assets/source/setup-guide/app/constants.js:193 +msgid "Resale marketplaces are not allowed" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:197 +msgid "Affiliate links are not allowed" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:201 +msgid "Account does not meet the website requirements for verification" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:205 +msgid "Account does not meet the product requirements for verification" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:209 +msgid "Account does not meet the brand reputation criteria for verification" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:213 +msgid "The template of the website is incomplete" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:217 +msgid "Merchant does not meet community guidelines" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:221 +msgid "Merchant has exceeded number of reported ads" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:225 +msgid "Merchant has exceeded number of user reports" +msgstr "" + +#: assets/source/setup-guide/app/constants.js:229 +msgid "Merchant does not meet minimum product requirements" msgstr "" #: assets/source/setup-guide/app/steps/AdvancedSettings.js:49 @@ -977,145 +1245,176 @@ msgid "Erase Plugin Data" msgstr "" #. translators: %s: error reason returned by Pinterest when verifying website claim fail. -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:51 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:54 msgid "We were unable to verify this domain. %s" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:134 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:141 msgid "Couldn’t verify your domain." msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:151 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:158 msgid "Start verification" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:155 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:159 msgid "Verifying…" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:156 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:160 msgid "Try again" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:157 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:161 msgid "Verified" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:175 -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:188 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:181 +#: assets/source/setup-guide/app/steps/SetupTracking.js:273 +msgid "Try Again" +msgstr "" + +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:182 +#: assets/source/setup-guide/app/steps/SetupTracking.js:274 +msgid "Complete Setup" +msgstr "" + +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:220 +#: assets/source/setup-guide/app/steps/SetupTracking.js:320 +msgid "There was a problem connecting the advertiser." +msgstr "" + +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:232 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:245 #: assets/source/setup-guide/app/views/WizardApp.js:77 msgid "Claim your website" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:179 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:236 msgid "Step Two" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:192 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:249 msgid "Verified domain" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:212 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:270 msgid "Verify your domain to claim your website" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:218 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:276 msgid "This will allow access to analytics for the Pins you publish from your site, the analytics on Pins that other people create from your site, and let people know where they can find more of your content." msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:247 -#: assets/source/setup-guide/app/steps/SetupAccount.js:243 -#: assets/source/setup-guide/app/steps/SetupTracking.js:270 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:306 +#: assets/source/setup-guide/app/steps/SetupAccount.js:247 +#: assets/source/setup-guide/app/steps/SetupTracking.js:272 msgid "Continue" msgstr "" -#: assets/source/setup-guide/app/steps/SetupAccount.js:94 +#: assets/source/setup-guide/app/steps/components/AdsCreditsPromo.js:57 +msgid "As a new Pinterest customer, you can get $125 in free ad credits when you successfully set up Pinterest for WooCommerce and spend $15 on Pinterest Ads. Pinterest Terms and conditions apply." +msgstr "" + +#: assets/source/setup-guide/app/steps/SetupAccount.js:97 msgid "Couldn’t retrieve your linked business accounts." msgstr "" -#: assets/source/setup-guide/app/steps/SetupAccount.js:106 +#: assets/source/setup-guide/app/steps/SetupAccount.js:109 #: assets/source/setup-guide/app/views/WizardApp.js:62 msgid "Set up your business account" msgstr "" -#: assets/source/setup-guide/app/steps/SetupAccount.js:110 +#: assets/source/setup-guide/app/steps/SetupAccount.js:113 msgid "Step One" msgstr "" -#: assets/source/setup-guide/app/steps/SetupAccount.js:119 +#: assets/source/setup-guide/app/steps/SetupAccount.js:122 msgid "Pinterest business account" msgstr "" -#: assets/source/setup-guide/app/steps/SetupAccount.js:123 +#: assets/source/setup-guide/app/steps/SetupAccount.js:126 msgid "Linked account" msgstr "" -#: assets/source/setup-guide/app/steps/SetupAccount.js:129 +#: assets/source/setup-guide/app/steps/SetupAccount.js:132 msgid "Set up a free Pinterest business account to get access to analytics on your Pins and the ability to run ads. This requires agreeing to our advertising guidelines and following our merchant guidelines." msgstr "" -#: assets/source/setup-guide/app/steps/SetupAccount.js:204 +#: assets/source/setup-guide/app/steps/SetupAccount.js:208 msgid "Or, create a new Pinterest account" msgstr "" -#: assets/source/setup-guide/app/steps/SetupAccount.js:231 +#: assets/source/setup-guide/app/steps/SetupAccount.js:235 msgid "Or, convert your personal account" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:60 +#: assets/source/setup-guide/app/steps/SetupPins.js:73 msgid "Publish Pins and Rich Pins" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:64 +#: assets/source/setup-guide/app/steps/SetupPins.js:77 msgid "Rich Pins are a type of organic Pin that automatically sync information from your website to your Pins. You can identify Rich Pins by the extra information above and below the image on closeup and the bold title in your feed. If something changes on the original website, the Rich Pin updates to reflect that change." msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:80 +#: assets/source/setup-guide/app/steps/SetupPins.js:93 msgid "Tracking" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:86 +#: assets/source/setup-guide/app/steps/SetupPins.js:99 msgid "Track conversions" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:92 +#: assets/source/setup-guide/app/steps/SetupPins.js:105 msgid "Gather analytics for Pins you publish and Pins users create from your site." msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:109 +#: assets/source/setup-guide/app/steps/SetupPins.js:122 msgid "Enhanced Match support" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:115 -msgid "Matches conversion data with the person responsible for the conversion and lets you track cross-device checkouts. Requires Track Conversion option to be enabled." +#: assets/source/setup-guide/app/steps/SetupPins.js:129 +msgid "Matches conversion data with the person responsible for the conversion and lets you track cross-device checkouts. Requires Track Conversion option to be enabled. See more" +msgstr "" + +#: assets/source/setup-guide/app/steps/SetupPins.js:176 +msgid "Automatic Enhanced Match support" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:143 +#: assets/source/setup-guide/app/steps/SetupPins.js:182 +msgid "Uses hashed information that your customers have already provided to your business to help match more of your website visitors and conversions to people on Pinterest. Enabling it may improve the performance of your campaigns and can help increase the size of your Pinterest tag audiences." +msgstr "" + +#: assets/source/setup-guide/app/steps/SetupPins.js:206 +msgid "Manage information shared on Pinterest Ads Manager " +msgstr "" + +#: assets/source/setup-guide/app/steps/SetupPins.js:235 msgid "Rich Pins" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:149 +#: assets/source/setup-guide/app/steps/SetupPins.js:241 msgid "Add Rich Pins for Products" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:155 +#: assets/source/setup-guide/app/steps/SetupPins.js:247 msgid "Automatically create and update rich pins on Pinterest for all synced products." msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:172 +#: assets/source/setup-guide/app/steps/SetupPins.js:264 msgid "Add Rich Pins for Posts" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:178 +#: assets/source/setup-guide/app/steps/SetupPins.js:270 msgid "Automatically create and update rich pins on Pinterest for posts." msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:198 -#: assets/source/setup-guide/app/steps/SetupPins.js:204 +#: assets/source/setup-guide/app/steps/SetupPins.js:290 +#: assets/source/setup-guide/app/steps/SetupPins.js:296 msgid "Save to Pinterest" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:210 +#: assets/source/setup-guide/app/steps/SetupPins.js:302 msgid "Adds a ‘Save’ button on images allowing customers to save things straight from your website to Pinterest." msgstr "" @@ -1131,148 +1430,143 @@ msgstr "" msgid "Enable Product Sync" msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:127 +#: assets/source/setup-guide/app/steps/SetupTracking.js:129 msgid "Couldn’t retrieve your advertisers." msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:185 +#: assets/source/setup-guide/app/steps/SetupTracking.js:187 msgid "Couldn’t retrieve your tags." msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:271 -msgid "Try Again" -msgstr "" - -#: assets/source/setup-guide/app/steps/SetupTracking.js:272 -msgid "Complete Setup" -msgstr "" - -#: assets/source/setup-guide/app/steps/SetupTracking.js:317 -msgid "There was a problem connecting the advertiser." -msgstr "" - -#: assets/source/setup-guide/app/steps/SetupTracking.js:329 -#: assets/source/setup-guide/app/steps/SetupTracking.js:346 -#: assets/source/setup-guide/app/views/WizardApp.js:83 +#: assets/source/setup-guide/app/steps/SetupTracking.js:332 +#: assets/source/setup-guide/app/steps/SetupTracking.js:349 msgid "Track conversions with the Pinterest tag" msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:333 +#: assets/source/setup-guide/app/steps/SetupTracking.js:336 msgid "Step Three" msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:342 +#: assets/source/setup-guide/app/steps/SetupTracking.js:345 msgid "Select your advertiser and tag" msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:353 -msgid "The Pinterest tag is a piece of JavaScript code you put on your website to gather conversion insights and build audiences to target based on actions people have taken on your site." +#: assets/source/setup-guide/app/steps/SetupTracking.js:357 +msgid "The Pinterest tag is a piece of JavaScript code you put on your website to gather conversion insights and build audiences to target based on actions people have taken on your site." msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:359 -msgid "Using conversion tags means you agree to our" +#: assets/source/setup-guide/app/steps/SetupTracking.js:381 +msgid "Using conversion tags means you agree to our Ad Guidelines and Ad Data Terms." msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:373 -msgid "Ad Guidelines" +#: assets/source/setup-guide/app/steps/SetupTracking.js:419 +msgid "Automatic Enhanced Match is enabled by default to match more of your website visitors and conversions to people on Pinterest. You can manage this in Settings." msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:378 -msgid "and" -msgstr "" - -#: assets/source/setup-guide/app/steps/SetupTracking.js:389 -msgid "Ad Data Terms" -msgstr "" - -#: assets/source/setup-guide/app/steps/SetupTracking.js:415 +#: assets/source/setup-guide/app/steps/SetupTracking.js:456 msgid "Advertiser" msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:439 +#: assets/source/setup-guide/app/steps/SetupTracking.js:480 msgid "Select the advertiser for which you would like to install a tracking snippet." msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:452 +#: assets/source/setup-guide/app/steps/SetupTracking.js:493 msgid "Tracking Tag" msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:481 +#: assets/source/setup-guide/app/steps/SetupTracking.js:522 msgid "Select the tracking tag to use." msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:498 +#: assets/source/setup-guide/app/steps/SetupTracking.js:539 msgid "In order to proceed you need to read and accept the contents of the Pinterest Advertising Agreement." msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:506 +#: assets/source/setup-guide/app/steps/SetupTracking.js:547 msgid "I accept the Pinterest Advertising Agreement" msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:553 +#: assets/source/setup-guide/app/steps/SetupTracking.js:594 msgid "An error occurred while attempting to fetch Advertisers & Tags from Pinterest. Please try again." msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:69 +#: assets/source/setup-guide/app/views/LandingPageApp.js:75 msgid "Get your products in front of more than 400M people on Pinterest" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:76 +#: assets/source/setup-guide/app/views/LandingPageApp.js:82 msgid "Pinterest is a visual discovery engine people use to find inspiration for their lives! More than 400 million people have saved more than 300 billion Pins, making it easier to turn inspiration into their next purchase." msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:84 +#: assets/source/setup-guide/app/views/LandingPageApp.js:90 msgid "Get started" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:90 +#: assets/source/setup-guide/app/views/LandingPageApp.js:96 msgid "By clicking ‘Get started’, you agree to our Terms of Service." msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:134 +#: assets/source/setup-guide/app/views/LandingPageApp.js:176 +msgid "Try Pinterest for WooCommerce and get $125 in ad credits!" +msgstr "" + +#: assets/source/setup-guide/app/views/LandingPageApp.js:183 +msgid "To help you get started with Pinterest Ads, new Pinterest customers can get $125 in ad credits when they have successfully set up Pinterest for WooCommerce and spend $15 on Pinterest Ads. Pinterest Terms and conditions apply." +msgstr "" + +#: assets/source/setup-guide/app/views/LandingPageApp.js:219 msgid "Sync your catalog" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:138 +#: assets/source/setup-guide/app/views/LandingPageApp.js:223 msgid "Connect your store to seamlessly sync your product catalog with Pinterest and create rich pins for each item. Your pins are kept up to date with daily automatic updates." msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:148 +#: assets/source/setup-guide/app/views/LandingPageApp.js:233 msgid "Increase organic reach" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:152 +#: assets/source/setup-guide/app/views/LandingPageApp.js:237 msgid "Pinterest users can easily discover, save and buy products from your website without any advertising spend from you. Track your performance with the Pinterest tag." msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:162 +#: assets/source/setup-guide/app/views/LandingPageApp.js:247 msgid "Create a storefront on Pinterest" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:166 +#: assets/source/setup-guide/app/views/LandingPageApp.js:251 msgid "Syncing your catalog creates a Shop tab on your Pinterest profile which allows Pinterest users to easily discover your products." msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:190 +#: assets/source/setup-guide/app/views/LandingPageApp.js:275 msgid "Frequently asked questions" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:197 +#: assets/source/setup-guide/app/views/LandingPageApp.js:282 msgid "Why am I getting an “Account not connected” error message?" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:201 +#: assets/source/setup-guide/app/views/LandingPageApp.js:286 msgid "Your password might have changed recently. Click Reconnect Pinterest Account and follow the instructions on screen to restore the connection." msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:208 +#: assets/source/setup-guide/app/views/LandingPageApp.js:293 msgid "I have more than one Pinterest Advertiser account. Can I connect my WooCommerce store to multiple Pinterest Advertiser accounts?" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:212 +#: assets/source/setup-guide/app/views/LandingPageApp.js:297 msgid "Only one Pinterest advertiser account can be linked to each WooCommerce store. If you want to connect a different Pinterest advertiser account you will need to either Disconnect the existing Pinterest Advertiser account from your current WooCommerce store and connect a different Pinterest Advertiser account, or Create another WooCommerce store and connect the additional Pinterest Advertiser account." msgstr "" +#: assets/source/setup-guide/app/views/LandingPageApp.js:304 +msgid "How do I redeem the $125 ad credit from Pinterest?" +msgstr "" + +#: assets/source/setup-guide/app/views/LandingPageApp.js:308 +msgid "To be eligible and redeem the $125 ad credit from Pinterest, you must complete the setup of Pinterest for WooCommerce, set up your billing with Pinterest Ads manager, and spend $15 with Pinterest ads. Ad credits may vary by country and is subject to availability. Credits may take up to 24 hours to be credited to the user. Each user is only eligible to receive the ad credits once." +msgstr "" + #: assets/source/setup-guide/index.js:47 msgid "Marketing" msgstr "" @@ -1284,15 +1578,3 @@ msgstr "" #: assets/source/setup-guide/index.js:108 msgid "Products Catalog" msgstr "" - -#: assets/source/setup-task/index.js:19 -msgid "Setup Pinterest for WooCommerce" -msgstr "" - -#: assets/source/setup-task/index.js:28 -msgid "Connect your store to Pinterest to sync products and track conversions." -msgstr "" - -#: assets/source/setup-task/index.js:32 -msgid "5 minutes" -msgstr "" diff --git a/package-lock.json b/package-lock.json index e7e4eb1e2..b7d06234f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23925,9 +23925,9 @@ "dev": true }, "prettier": { - "version": "npm:wp-prettier@2.2.1-beta-1", - "resolved": "https://registry.npmjs.org/wp-prettier/-/wp-prettier-2.2.1-beta-1.tgz", - "integrity": "sha512-+JHkqs9LC/JPp51yy1hzs3lQ7qeuWCwOcSzpQNeeY/G7oSpnF61vxt7hRh87zNRTr6ob2ndy0W8rVzhgrcA+Gw==", + "version": "npm:wp-prettier@2.8.5", + "resolved": "https://registry.npmjs.org/wp-prettier/-/wp-prettier-2.8.5.tgz", + "integrity": "sha512-gkphzYtVksWV6D7/V530bTehKkhrABUru/Gy4reOLOHJoKH4i9lcE1SxqU2VDxC3gCOx/Nk9alZmWk6xL/IBCw==", "dev": true }, "prettier-linter-helpers": { diff --git a/src/API/AuthDisconnect.php b/src/API/AuthDisconnect.php index 0e97562a3..c6b5edfad 100644 --- a/src/API/AuthDisconnect.php +++ b/src/API/AuthDisconnect.php @@ -8,6 +8,7 @@ namespace Automattic\WooCommerce\Pinterest\API; +use Pinterest_For_Woocommerce; use WP_Error; if ( ! defined( 'ABSPATH' ) ) { @@ -41,7 +42,7 @@ public function __construct() { */ public function handle_disconnect() { return array( - 'disconnected' => Pinterest_For_Woocommerce()::disconnect(), + 'disconnected' => Pinterest_For_Woocommerce::disconnect(), ); } } diff --git a/src/API/Options.php b/src/API/Options.php index 415ffea6a..cc404a0ea 100644 --- a/src/API/Options.php +++ b/src/API/Options.php @@ -8,6 +8,7 @@ namespace Automattic\WooCommerce\Pinterest\API; +use Pinterest_For_Woocommerce; use \WP_Error; use \WP_REST_Server; use \WP_REST_Request; From e5da371ff323560c5818b04ab7a7d1ae0918187c Mon Sep 17 00:00:00 2001 From: Dima Date: Tue, 23 May 2023 23:16:46 +0300 Subject: [PATCH 03/36] Since corresponding v5 API is not ready yet, faking the health check endpoint to have it temporarily working and keep the progress going. --- src/API/HealthCheck.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/API/HealthCheck.php b/src/API/HealthCheck.php index 423d077bc..03ad297eb 100644 --- a/src/API/HealthCheck.php +++ b/src/API/HealthCheck.php @@ -41,6 +41,10 @@ public function __construct() { */ public function health_check() { + return array( + 'status' => 'approved', + ); + try { $response = array(); From ecf31511fef1ebbed83991c2c355844d3a859121 Mon Sep 17 00:00:00 2001 From: Dima Date: Wed, 24 May 2023 23:00:12 +0300 Subject: [PATCH 04/36] Resolve code conflict. --- class-pinterest-for-woocommerce.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/class-pinterest-for-woocommerce.php b/class-pinterest-for-woocommerce.php index 18d5ac8cb..84072866a 100644 --- a/class-pinterest-for-woocommerce.php +++ b/class-pinterest-for-woocommerce.php @@ -808,6 +808,8 @@ public static function get_middleware_url( $context = 'login', $args = array() ) $state = http_build_query( $state_params ); + // phpcs:ignore Squiz.Commenting.InlineComment.InvalidEndChar + // nosemgrep: audit.php.wp.security.xss.query-arg return self::get_connection_proxy_url() . 'connect/' . PINTEREST_FOR_WOOCOMMERCE_WOO_CONNECT_SERVICE . '?' . $state; } From c014745604d9a963f81e75bb21c7affde41c9b61 Mon Sep 17 00:00:00 2001 From: Dima Date: Tue, 30 May 2023 01:18:10 +0300 Subject: [PATCH 05/36] Adjusting Domain Verification api to use API v5 to update integration data. Add unit tests on that method. --- class-pinterest-for-woocommerce.php | 69 +++++- src/API/APIV5.php | 47 ++++ src/API/AdvertiserConnect.php | 26 ++- src/API/DomainVerification.php | 40 +++- tests/Unit/PinterestForWoocommerceTest.php | 236 +++++++++++++++++++++ 5 files changed, 400 insertions(+), 18 deletions(-) diff --git a/class-pinterest-for-woocommerce.php b/class-pinterest-for-woocommerce.php index 84072866a..be6725950 100644 --- a/class-pinterest-for-woocommerce.php +++ b/class-pinterest-for-woocommerce.php @@ -833,7 +833,24 @@ public function maybe_inject_verification_code() { * * @since x.x.x * - * @return array + * @return array { + * Integration data returned by Pinterest. + * + * @type string $id ID of the integration (string all digits). + * @type string $external_business_id External business ID for the integration. + * @type string $connected_merchant_id Connected merchant ID for the integration. + * @type string $connected_user_id Connected user ID for the integration. + * @type string $connected_advertiser_id Connected advertiser ID for the integration. + * @type string $connected_lba_id Connected LBA ID for the integration. + * @type string $connected_tag_id Connected tag ID for the integration. + * @type int $partner_access_token_expiry Partner access token expiry for the integration. + * @type int $partner_refresh_token_expiry Partner refresh token expiry for the integration. + * @type string $scopes Scopes for the integration. + * @type int $created_timestamp Created timestamp for the integration. + * @type int $updated_timestamp Updated timestamp for the integration. + * @type string $additional_id_1 Additional ID 1 for the integration. + * @type string $partner_metadata Partner metadata for the integration. + * } * @throws PinterestApiException */ public static function create_commerce_integration(): array { @@ -869,6 +886,56 @@ public static function create_commerce_integration(): array { return $response; } + /** + * Updates WC integration parameters with Pinterest. + * + * @since x.x.x + * + * @param string $external_business_id + * @param array { + * @type string $external_business_id External business ID for the integration. + * @type string $connected_merchant_id Connected merchant ID for the integration. + * @type string $connected_advertiser_id Connected advertiser ID for the integration. + * @type string $connected_lba_id Connected LBA ID for the integration. + * @type string $connected_tag_id Connected tag ID for the integration. + * @type string $partner_access_token Partner access token for the integration. + * @type string $partner_refresh_token Partner refresh token for the integration. + * @type string $partner_primary_email Partner primary email for the integration. + * @type int $partner_access_token_expiry Partner access token expiry for the integration. + * @type int $partner_refresh_token_expiry Partner refresh token expiry for the integration. + * @type string $scopes Scopes for the integration. + * @type string $additional_id_1 Additional ID 1 for the integration. + * @type string $partner_metadata Partner metadata for the integration. + * } + * + * @return array { + * Integration data returned by Pinterest. + * + * @type string $id ID of the integration (string all digits). + * @type string $external_business_id External business ID for the integration. + * @type string $connected_merchant_id Connected merchant ID for the integration. + * @type string $connected_user_id Connected user ID for the integration. + * @type string $connected_advertiser_id Connected advertiser ID for the integration. + * @type string $connected_lba_id Connected LBA ID for the integration. + * @type string $connected_tag_id Connected tag ID for the integration. + * @type int $partner_access_token_expiry Partner access token expiry for the integration. + * @type int $partner_refresh_token_expiry Partner refresh token expiry for the integration. + * @type string $scopes Scopes for the integration. + * @type int $created_timestamp Created timestamp for the integration. + * @type int $updated_timestamp Updated timestamp for the integration. + * @type string $additional_id_1 Additional ID 1 for the integration. + * @type string $partner_metadata Partner metadata for the integration. + * } + * @throws PinterestApiException + */ + public static function update_commerce_integration( string $external_business_id, array $data ): array { + return Pinterest\API\APIV5::make_request( + "integrations/commerce/{$external_business_id}", + 'PATCH', + json_encode( $data ) + ); + } + /** * Disconnects WC from Pinterest. * diff --git a/src/API/APIV5.php b/src/API/APIV5.php index 1ae5a8bc4..573a5d6ad 100644 --- a/src/API/APIV5.php +++ b/src/API/APIV5.php @@ -9,6 +9,7 @@ namespace Automattic\WooCommerce\Pinterest\API; +use Automattic\WooCommerce\Pinterest\PinterestApiException; use Automattic\WooCommerce\Pinterest\PinterestApiException as ApiException; if ( ! defined( 'ABSPATH' ) ) { @@ -116,4 +117,50 @@ public static function get_advertisers( $pinterest_user = null ) { public static function get_advertiser_tags( $ad_account_id ) { return self::make_request( "ad_accounts/{$ad_account_id}/conversion_tags", 'GET' ); } + + /** + * Returns Pinterest user verification code for website verification. + * + * @since x.x.x + * + * @return array { + * Data needed to verify a website. + * + * @type string $verification_code Code to check against the user claiming the website. + * @type string $dns_txt_record DNS TXT record to check against for the website to be claimed. + * @type string $metatag META tag the verification process searches for the website to be claimed. + * @type string $filename File expected to find on the website being claimed. + * @type string $file_content A full html file to upload to the website in order for it to be claimed. + * } + * @throws PinterestApiException + */ + public static function domain_verification_data(): array { + return self::make_request( 'user_account/websites/verification' ); + } + + /** + * Sends domain verification request to Pinterest. + * + * @since x.x.x + * + * @param string $domain Domain to verify. + * @return array { + * Data returned by Pinterest after the verification request. + * + * @type string $website Website with path or domain only. + * @type string $status Status of the verification process. + * @type string $verified_at UTC timestamp when the verification happened - sometimes missing. + * } + * @throws PinterestApiException + */ + public static function domain_metatag_verification_request( string $domain ): array { + return self::make_request( + 'user_account/websites', + 'POST', + array( + 'website' => $domain, + 'verification_method' => 'METATAG', + ) + ); + } } diff --git a/src/API/AdvertiserConnect.php b/src/API/AdvertiserConnect.php index dbcc024c1..0460a7bb3 100644 --- a/src/API/AdvertiserConnect.php +++ b/src/API/AdvertiserConnect.php @@ -11,6 +11,9 @@ use Automattic\WooCommerce\Pinterest\AdCredits; use Automattic\WooCommerce\Pinterest\Billing; use Automattic\WooCommerce\Pinterest\Utilities\Utilities; +use Exception; +use Pinterest_For_Woocommerce; +use Throwable; use \WP_REST_Server; use \WP_REST_Request; use \WP_Error; @@ -59,7 +62,8 @@ public function connect_advertiser( WP_REST_Request $request ) { } if ( $enable_aem ) { - self::enable_aem_tag( $tag_id ); + // @TODO: We do not have an API v5 analog for this call. Commenting it out temporarily. + #self::enable_aem_tag( $tag_id ); } $is_connected = Pinterest_For_Woocommerce()::get_data( 'is_advertiser_connected' ); @@ -72,7 +76,7 @@ public function connect_advertiser( WP_REST_Request $request ) { ); } - // Connect new advertiser and tag. + // Update integration with new advertiser and a tag. return self::connect_advertiser_and_tag( $advertiser_id, $tag_id ); } catch ( \Throwable $th ) { @@ -95,14 +99,16 @@ public function connect_advertiser( WP_REST_Request $request ) { */ public static function connect_advertiser_and_tag( $advertiser_id, $tag_id ) { - $response = Base::connect_advertiser( $advertiser_id, $tag_id ); - - if ( 'success' !== $response['status'] ) { - throw new \Exception( esc_html__( 'The advertiser could not be connected to Pinterest.', 'pinterest-for-woocommerce' ), 400 ); - } + $external_business_id = Pinterest_For_Woocommerce()::get_data( 'external_business_id' ); + $data = array( + 'connected_advertiser_id' => $advertiser_id, + 'connected_tag_id' => $tag_id, + ); - if ( $advertiser_id !== $response['data']->advertiser_id ) { - throw new \Exception( esc_html__( 'Incorrect advertiser ID.', 'pinterest-for-woocommerce' ), 400 ); + try { + $response = Pinterest_For_Woocommerce::update_commerce_integration( $external_business_id, $data ); + } catch ( Throwable $th ) { + throw new Exception( $th->getMessage(), 400 ); } Pinterest_For_Woocommerce()::save_data( 'is_advertiser_connected', true ); @@ -130,7 +136,7 @@ public static function connect_advertiser_and_tag( $advertiser_id, $tag_id ) { UserInteraction::flush_options(); return array( - 'connected' => $response['data']->advertiser_id, + 'connected' => $response['connected_advertiser_id'], 'reconnected' => true, ); } diff --git a/src/API/DomainVerification.php b/src/API/DomainVerification.php index 258a458f1..a55f1b122 100644 --- a/src/API/DomainVerification.php +++ b/src/API/DomainVerification.php @@ -10,8 +10,11 @@ use Automattic\WooCommerce\Pinterest\Logger as Logger; -use \WP_REST_Server; -use \WP_REST_Request; +use Automattic\WooCommerce\Pinterest\PinterestApiException; +use Exception; +use Pinterest_For_Woocommerce; +use WP_Error; +use WP_REST_Server; if ( ! defined( 'ABSPATH' ) ) { exit; @@ -35,7 +38,7 @@ class DomainVerification extends VendorAPI { public function __construct() { $this->base = 'domain_verification'; - $this->endpoint_callback = 'handle_verification'; + $this->endpoint_callback = 'maybe_handle_verification'; $this->methods = WP_REST_Server::EDITABLE; $this->register_routes(); @@ -44,14 +47,37 @@ public function __construct() { /** * Handle domain verification by triggering the realtime verification process - * using the Pinterst API. + * using the Pinterest API. + * + * @since x.x.x * * @return mixed * - * @throws \Exception PHP Exception. + * @throws Exception PHP Exception. */ - public function handle_verification() { - return self::trigger_domain_verification(); + public function maybe_handle_verification() { + try { + $result = array(); + if ( ! Pinterest_For_Woocommerce::is_domain_verified() ) { + $domain_verification_data = APIV5::domain_verification_data(); + Pinterest_For_Woocommerce()::save_data( 'verification_data', $domain_verification_data ); + + $parsed_website = wp_parse_url( get_home_url() ); + $result = APIV5::domain_metatag_verification_request( $parsed_website['host'] . $parsed_website['path'] ); + } + $account_data = Pinterest_For_Woocommerce()::update_account_data(); + return array_merge( $result, array( 'account_data' => $account_data ) ); + } catch ( PinterestApiException $th ) { + return new WP_Error( + 'pinterest-for-woocommerce_verification_error', + $th->getMessage(), + array( + 'status' => $th->getCode(), + 'pinterest_code' => method_exists( $th, 'get_pinterest_code' ) ? $th->get_pinterest_code() : 0, + ) + ); + } + // return self::trigger_domain_verification(); } diff --git a/tests/Unit/PinterestForWoocommerceTest.php b/tests/Unit/PinterestForWoocommerceTest.php index 2747bd021..ff2b559a7 100644 --- a/tests/Unit/PinterestForWoocommerceTest.php +++ b/tests/Unit/PinterestForWoocommerceTest.php @@ -1,5 +1,6 @@ assertEquals( 10, has_action( 'init', [ RefreshToken::class, 'schedule_event' ] ) ); } + + public function test_update_commerce_integration_returns_successful_response() { + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) { + return array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array_merge( + array( + 'id' => '9876543210123456789', + 'connected_user_id' => 'cud-123456789', + 'created_timestamp' => 123456789, + 'updated_timestamp' => 987654321, + ), + array_filter( + json_decode( $parsed_args['body'], true ), + function ( $key ) { + return ! in_array( + $key, + array( 'partner_access_token', 'partner_refresh_token', 'partner_primary_email' ) + ); + }, + ARRAY_FILTER_USE_KEY + ) + ), + ), + 'response' => array( + 'code' => 200, + ), + 'cookies' => array(), + 'filename' => '', + 'http_response' => new WP_HTTP_Requests_Response( + new WpOrg\Requests\Response(), + '' + ), + ); + }, + 10, + 3 + ); + + $external_business_id = 'ebi-123456789'; + $data = array( + 'external_business_id' => 'ebi-123456789', + 'connected_merchant_id' => 'cmd-123456789', + 'connected_advertiser_id' => 'cai-123456789', + 'connected_lba_id' => 'cli-123456789', + 'connected_tag_id' => 'cti-123456789', + 'partner_access_token' => 'pat-123456789', + 'partner_refresh_token' => 'prt-123456789', + 'partner_primary_email' => 'ppe-123456789', + 'partner_access_token_expiry' => 9876543210, + 'partner_refresh_token_expiry' => 9876543210, + 'scopes' => 's-c-o-p-e-s', + 'additional_id_1' => 'ai1-123456789', + 'partner_metadata' => 'partner-meta-data', + ); + $response = Pinterest_For_Woocommerce::update_commerce_integration( $external_business_id, $data ); + + $this->assertEquals( + array( + 'id' => '9876543210123456789', + 'external_business_id' => 'ebi-123456789', + 'connected_merchant_id' => 'cmd-123456789', + 'connected_user_id' => 'cud-123456789', + 'connected_advertiser_id' => 'cai-123456789', + 'connected_lba_id' => 'cli-123456789', + 'connected_tag_id' => 'cti-123456789', + 'partner_access_token_expiry' => 9876543210, + 'partner_refresh_token_expiry' => 9876543210, + 'scopes' => 's-c-o-p-e-s', + 'created_timestamp' => 123456789, + 'updated_timestamp' => 987654321, + 'additional_id_1' => 'ai1-123456789', + 'partner_metadata' => 'partner-meta-data', + ), + $response + ); + } + + public function test_update_commerce_integration_returns_integration_not_found() { + $this->expectException( PinterestApiException::class ); + $this->expectExceptionCode( 404 ); + $this->expectExceptionMessage( 'Sorry! We could not find your integration.' ); + + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) { + return array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + 'code' => 4180, + 'message' => 'Sorry! We could not find your integration.', + ) + ), + 'response' => array( + 'code' => 404, + ), + 'cookies' => array(), + 'filename' => '', + 'http_response' => new WP_HTTP_Requests_Response( + new WpOrg\Requests\Response(), + '' + ), + ); + }, + 10, + 3 + ); + + $data = array( + 'external_business_id' => 'ebi-123456789', + 'connected_merchant_id' => 'cmd-123456789', + 'connected_advertiser_id' => 'cai-123456789', + 'connected_lba_id' => 'cli-123456789', + 'connected_tag_id' => 'cti-123456789', + 'partner_access_token' => 'pat-123456789', + 'partner_refresh_token' => 'prt-123456789', + 'partner_primary_email' => 'ppe-123456789', + 'partner_access_token_expiry' => 9876543210, + 'partner_refresh_token_expiry' => 9876543210, + 'scopes' => 's-c-o-p-e-s', + 'additional_id_1' => 'ai1-123456789', + 'partner_metadata' => 'partner-meta-data', + ); + Pinterest_For_Woocommerce::update_commerce_integration( 'ebi-123456789', $data ); + } + + public function test_update_commerce_integration_returns_cant_access_this_integration_metadata() { + $this->expectException( PinterestApiException::class ); + $this->expectExceptionCode( 409 ); + $this->expectExceptionMessage( 'Can\'t access this integration metadata.' ); + + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) { + return array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + 'code' => 4182, + 'message' => 'Can\'t access this integration metadata.', + ) + ), + 'response' => array( + 'code' => 409, + ), + 'cookies' => array(), + 'filename' => '', + 'http_response' => new WP_HTTP_Requests_Response( + new WpOrg\Requests\Response(), + '' + ), + ); + }, + 10, + 3 + ); + + $data = array( + 'external_business_id' => 'ebi-123456789', + 'connected_merchant_id' => 'cmd-123456789', + 'connected_advertiser_id' => 'cai-123456789', + 'connected_lba_id' => 'cli-123456789', + 'connected_tag_id' => 'cti-123456789', + 'partner_access_token' => 'pat-123456789', + 'partner_refresh_token' => 'prt-123456789', + 'partner_primary_email' => 'ppe-123456789', + 'partner_access_token_expiry' => 9876543210, + 'partner_refresh_token_expiry' => 9876543210, + 'scopes' => 's-c-o-p-e-s', + 'additional_id_1' => 'ai1-123456789', + 'partner_metadata' => 'partner-meta-data', + ); + Pinterest_For_Woocommerce::update_commerce_integration( 'ebi-123456789', $data ); + } + + public function test_update_commerce_integration_returns_unexpected_error() { + $this->expectException( PinterestApiException::class ); + $this->expectExceptionCode( 0 ); + $this->expectExceptionMessage( 'Any other message from Pinterest which falls under Unexpected error.' ); + + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) { + return array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + 'code' => 0, + 'message' => 'Any other message from Pinterest which falls under Unexpected error.', + ) + ), + 'response' => array( + 'code' => 0, + ), + 'cookies' => array(), + 'filename' => '', + 'http_response' => new WP_HTTP_Requests_Response( + new WpOrg\Requests\Response(), + '' + ), + ); + }, + 10, + 3 + ); + + $data = array( + 'external_business_id' => 'ebi-123456789', + 'connected_merchant_id' => 'cmd-123456789', + 'connected_advertiser_id' => 'cai-123456789', + 'connected_lba_id' => 'cli-123456789', + 'connected_tag_id' => 'cti-123456789', + 'partner_access_token' => 'pat-123456789', + 'partner_refresh_token' => 'prt-123456789', + 'partner_primary_email' => 'ppe-123456789', + 'partner_access_token_expiry' => 9876543210, + 'partner_refresh_token_expiry' => 9876543210, + 'scopes' => 's-c-o-p-e-s', + 'additional_id_1' => 'ai1-123456789', + 'partner_metadata' => 'partner-meta-data', + ); + Pinterest_For_Woocommerce::update_commerce_integration( 'ebi-123456789', $data ); + } } From dbb3323b6d2a10de2cae7fb4257ddb0b06d53976 Mon Sep 17 00:00:00 2001 From: Dima Date: Tue, 30 May 2023 01:32:32 +0300 Subject: [PATCH 06/36] Update method description. Minor code refactoring. --- src/API/AdvertiserConnect.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/API/AdvertiserConnect.php b/src/API/AdvertiserConnect.php index 0460a7bb3..fab0d8257 100644 --- a/src/API/AdvertiserConnect.php +++ b/src/API/AdvertiserConnect.php @@ -95,11 +95,17 @@ public function connect_advertiser( WP_REST_Request $request ) { * @param string $advertiser_id The ID of the advertiser. * @param string $tag_id The ID of the tag. * - * @throws \Exception PHP Exception. + * @return array { + * Updates Pinterest integration with the new advertiser and tag. + * + * @type string $connected The ID of the connected advertiser. + * @type bool $reconnected Whether the advertiser was reconnected. + * } + * @throws Exception PHP Exception. */ - public static function connect_advertiser_and_tag( $advertiser_id, $tag_id ) { + public static function connect_advertiser_and_tag( string $advertiser_id, string $tag_id ): array { - $external_business_id = Pinterest_For_Woocommerce()::get_data( 'external_business_id' ); + $external_business_id = Pinterest_For_Woocommerce::get_data( 'external_business_id' ); $data = array( 'connected_advertiser_id' => $advertiser_id, 'connected_tag_id' => $tag_id, @@ -111,10 +117,10 @@ public static function connect_advertiser_and_tag( $advertiser_id, $tag_id ) { throw new Exception( $th->getMessage(), 400 ); } - Pinterest_For_Woocommerce()::save_data( 'is_advertiser_connected', true ); + Pinterest_For_Woocommerce::save_data( 'is_advertiser_connected', true ); // At this stage we can check if the connected advertiser has billing setup. - $has_billing = Pinterest_For_Woocommerce()::add_billing_setup_info_to_account_data(); + $has_billing = Pinterest_For_Woocommerce::add_billing_setup_info_to_account_data(); /* * If the advertiser does not have a correct billing lets check for the setup frequently for the next hour. From c6eb30a12da98fb49be82f9ff72b5e1d7c4e0a15 Mon Sep 17 00:00:00 2001 From: Dima Date: Tue, 30 May 2023 01:35:08 +0300 Subject: [PATCH 07/36] Add namespace to the test file, minor code refactoring. --- tests/Unit/PinterestForWoocommerceTest.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/Unit/PinterestForWoocommerceTest.php b/tests/Unit/PinterestForWoocommerceTest.php index ff2b559a7..32db0429a 100644 --- a/tests/Unit/PinterestForWoocommerceTest.php +++ b/tests/Unit/PinterestForWoocommerceTest.php @@ -1,9 +1,15 @@ array(), 'filename' => '', 'http_response' => new WP_HTTP_Requests_Response( - new WpOrg\Requests\Response(), + new Response(), '' ), ); @@ -121,7 +127,7 @@ function ( $response, $parsed_args, $url ) { 'cookies' => array(), 'filename' => '', 'http_response' => new WP_HTTP_Requests_Response( - new WpOrg\Requests\Response(), + new Response(), '' ), ); @@ -172,7 +178,7 @@ function ( $response, $parsed_args, $url ) { 'cookies' => array(), 'filename' => '', 'http_response' => new WP_HTTP_Requests_Response( - new WpOrg\Requests\Response(), + new Response(), '' ), ); @@ -223,7 +229,7 @@ function ( $response, $parsed_args, $url ) { 'cookies' => array(), 'filename' => '', 'http_response' => new WP_HTTP_Requests_Response( - new WpOrg\Requests\Response(), + new Response(), '' ), ); From 652b14b6beff29d628634e8493042102761cd206 Mon Sep 17 00:00:00 2001 From: Dima Date: Wed, 31 May 2023 00:31:39 +0300 Subject: [PATCH 08/36] Add Advertiser Connect unit test. Add API V5 unit tests. Move common code into separate function not to duplicate the filter. --- src/API/APIV5.php | 55 ++++++++- src/API/Base.php | 23 +++- src/API/Tags.php | 18 ++- tests/Unit/Api/APIV5Test.php | 125 +++++++++++++++++++++ tests/Unit/Api/AdvertiserConnectTest.php | 111 ++++++++++++++++++ tests/Unit/PinterestForWoocommerceTest.php | 2 +- 6 files changed, 321 insertions(+), 13 deletions(-) create mode 100644 tests/Unit/Api/APIV5Test.php create mode 100644 tests/Unit/Api/AdvertiserConnectTest.php diff --git a/src/API/APIV5.php b/src/API/APIV5.php index 573a5d6ad..8173d970c 100644 --- a/src/API/APIV5.php +++ b/src/API/APIV5.php @@ -11,6 +11,7 @@ use Automattic\WooCommerce\Pinterest\PinterestApiException; use Automattic\WooCommerce\Pinterest\PinterestApiException as ApiException; +use Exception; if ( ! defined( 'ABSPATH' ) ) { exit; @@ -112,12 +113,64 @@ public static function get_advertisers( $pinterest_user = null ) { * * @param string $ad_account_id the advertiser_id to request the tags for. * - * @return mixed + * @return array + * @throws ApiException|Exception */ public static function get_advertiser_tags( $ad_account_id ) { return self::make_request( "ad_accounts/{$ad_account_id}/conversion_tags", 'GET' ); } + /** + * Create a tag for the given advertiser. + * @link https://developers.pinterest.com/docs/api/v5/#operation/conversion_tags/create + * + * @since x.x.x + * + * @param string $ad_account_id the advertiser_id to create a tag for. + * + * @return array { + * Tag object. + * + * @type string $ad_account_id Ad account ID. + * @type string $code_snippet Tag code snippet. + * @type ?string $enhanced_match_status The enhanced match status of the tag. + * @type string $id Tag ID. + * @type ?int $last_fired_time_ms Time for the last event fired. + * @type string $name Conversion tag name. + * @type string $status Entity status. + * @type string $version Version number. + * @type array $configs { + * Tag configuration. + * + * @type ?bool $aem_enabled Whether Automatic Enhanced Match email is enabled. + * @type ?int $md_frequency Metadata ingestion frequency. + * @type ?bool $aem_fnln_enabled Whether Automatic Enhanced Match name is enabled. + * @type ?bool $aem_ph_enabled Whether Automatic Enhanced Match phone is enabled. + * @type ?bool $aem_ge_enabled Whether Automatic Enhanced Match gender is enabled. + * @type ?bool $aem_db_enabled Whether Automatic Enhanced Match birthdate is enabled. + * @type ?bool $aem_loc_enabled Whether Automatic Enhanced Match location is enabled. + * } + * } + * @throws ApiException|Exception + */ + public static function create_tag( $ad_account_id ) { + $tag_name = self::get_tag_name(); + return self::make_request( + "ad_accounts/{$ad_account_id}/conversion_tags", + 'POST', + array( + 'name' => $tag_name, + 'aem_enabled' => true, + 'md_frequency' => 1, + 'aem_fnln_enabled' => true, + 'aem_ph_enabled' => true, + 'aem_ge_enabled' => true, + 'aem_db_enabled' => true, + 'ae_loc_enabled' => true, + ) + ); + } + /** * Returns Pinterest user verification code for website verification. * diff --git a/src/API/Base.php b/src/API/Base.php index 4a0db0f72..94ef6a23c 100644 --- a/src/API/Base.php +++ b/src/API/Base.php @@ -524,7 +524,7 @@ public static function get_advertiser_tag( $advertiser_id, $tag_id ) { */ public static function create_tag( $advertiser_id ) { - $tag_name = apply_filters( 'pinterest_for_woocommerce_default_tag_name', esc_html__( 'Auto-created by Pinterest for WooCommerce', 'pinterest-for-woocommerce' ) ); + $tag_name = static::get_tag_name(); return self::make_request( "advertisers/{$advertiser_id}/conversion_tags", @@ -814,4 +814,25 @@ public static function get_list_of_ads_supported_countries() { $request_url = 'advertisers/countries'; return self::make_request( $request_url, 'GET', array(), 'ads', DAY_IN_SECONDS ); } + + /** + * Generates a tag name. + * + * @since x.x.x + * + * @return string The tag name. + */ + protected static function get_tag_name(): string { + /** + * Filters the default tag name. + * + * @since Unknown + * + * @param string $tag_name The default tag name. + */ + return apply_filters( + 'pinterest_for_woocommerce_default_tag_name', + esc_html__( 'Auto-created by Pinterest for WooCommerce', 'pinterest-for-woocommerce' ) + ); + } } diff --git a/src/API/Tags.php b/src/API/Tags.php index c6110c408..6be8499ce 100644 --- a/src/API/Tags.php +++ b/src/API/Tags.php @@ -8,6 +8,8 @@ namespace Automattic\WooCommerce\Pinterest\API; +use Exception; +use Throwable; use \WP_REST_Server; use \WP_REST_Request; @@ -42,22 +44,18 @@ public function __construct() { * @throws \Exception PHP Exception. */ public function get_tags( WP_REST_Request $request ) { - try { - $ad_account_id = $request->get_param( 'advrtsr_id' ); - if ( ! $ad_account_id ) { - throw new \Exception( esc_html__( 'Advertiser missing', 'pinterest-for-woocommerce' ), 400 ); + throw new Exception( esc_html__( 'Advertiser missing', 'pinterest-for-woocommerce' ), 400 ); } - - $tags = APIV5::get_advertiser_tags( $ad_account_id ); - - /*if ( 'success' !== $response['status'] ) { - throw new \Exception( esc_html__( 'Response error', 'pinterest-for-woocommerce' ), 400 ); + try { + $tags = APIV5::get_advertiser_tags( $ad_account_id ); + } catch ( Throwable $th ) { + throw new Exception( esc_html__( 'Response error', 'pinterest-for-woocommerce' ), 400 ); } - $tags = (array) $response['data']; + /*$tags = (array) $response['data']; if ( empty( $tags ) ) { // No tag created yet. Lets create one. diff --git a/tests/Unit/Api/APIV5Test.php b/tests/Unit/Api/APIV5Test.php new file mode 100644 index 000000000..9a8992bb0 --- /dev/null +++ b/tests/Unit/Api/APIV5Test.php @@ -0,0 +1,125 @@ + array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + 'ad_account_id' => 'aai-1234567890', + 'code_snippet' => '', + 'id' => '9876543210123456789', + 'last_fired_time_ms' => 123456789, + 'name' => $parsed_args['body']['name'], + 'status' => 'ACTIVE', + 'version' => 'v1', + 'configs' => array( + 'aem_enabled' => true, + 'md_frequency' => 1, + 'aem_fnln_enabled' => true, + 'aem_ph_enabled' => true, + 'aem_ge_enabled' => true, + 'aem_db_enabled' => true, + 'ae_loc_enabled' => true, + ), + ) + ), + 'response' => array( + 'code' => 200, + ), + 'cookies' => array(), + 'filename' => '', + 'http_response' => new WP_HTTP_Requests_Response( + new Response(), + '' + ), + ); + }, + 10, + 3 + ); + + $output = APIV5::create_tag( 'aai-1234567890' ); + + $this->assertEquals( + 'Some tag name 42', + $output['name'] + ); + + // Check if enhanced match is enabled. + $this->assertEquals( + array( + 'aem_enabled' => true, + 'md_frequency' => 1, + 'aem_fnln_enabled' => true, + 'aem_ph_enabled' => true, + 'aem_ge_enabled' => true, + 'aem_db_enabled' => true, + 'ae_loc_enabled' => true, + ), + $output['configs'] + ); + } + + public function test_create_tag_returns_unexpected_error() { + $this->expectException( PinterestApiException::class ); + $this->expectExceptionCode( 0 ); + $this->expectExceptionMessage( 'Any other message from Pinterest which falls under Unexpected error.' ); + + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) { + return array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + 'code' => 0, + 'message' => 'Any other message from Pinterest which falls under Unexpected error.', + ) + ), + 'response' => array( + 'code' => 0, + ), + 'cookies' => array(), + 'filename' => '', + 'http_response' => new WP_HTTP_Requests_Response( + new Response(), + '' + ), + ); + }, + 10, + 3 + ); + + APIV5::create_tag( 'aai-1234567890' ); + } +} diff --git a/tests/Unit/Api/AdvertiserConnectTest.php b/tests/Unit/Api/AdvertiserConnectTest.php new file mode 100644 index 000000000..b6b85aad6 --- /dev/null +++ b/tests/Unit/Api/AdvertiserConnectTest.php @@ -0,0 +1,111 @@ + array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + "id" => "987654321234567890", + "external_business_id" => "cbi-1234567890", + "connected_merchant_id" => "cmi-1234567890", + "connected_user_id" => "cui-1234567890", + "connected_advertiser_id" => $parsed_args['body']['advertiser_id'], + "connected_lba_id" => "cli-1234567890", + "connected_tag_id" => $parsed_args['body']['tag_id'], + "partner_access_token_expiry" => 1621350033000, + "partner_refresh_token_expiry" => 1621350033000, + "scopes" => "s-c-o-p-e-s", + "created_timestamp" => 1621350033000, + "updated_timestamp" => 1621350033300, + "additional_id_1" => "ai1-1234567890", + "partner_metadata" => "partner-meta-data", + ) + ), + 'response' => array( + 'code' => 200, + ), + 'cookies' => array(), + 'filename' => '', + 'http_response' => new WP_HTTP_Requests_Response( + new Response(), + '' + ), + ); + }, + 10, + 3 + ); + + $output = AdvertiserConnect::connect_advertiser_and_tag( + 'ai-1234567890', + 'ti-1234567890' + ); + + $this->assertEquals( + array( + 'connected' => 'ai-1234567890', + 'reconnected' => true, + ), + $output + ); + } + + public function test_connect_advertiser_and_tag_returns_error() { + $this->expectException( Exception::class ); + $this->expectExceptionCode( 400 ); + $this->expectExceptionMessage( 'Sorry! We could not find your integration.' ); + + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) { + return array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + 'code' => 4180, + 'message' => 'Sorry! We could not find your integration.', + ) + ), + 'response' => array( + 'code' => 404, + ), + 'cookies' => array(), + 'filename' => '', + 'http_response' => new WP_HTTP_Requests_Response( + new Response(), + '' + ), + ); + }, + 10, + 3 + ); + + AdvertiserConnect::connect_advertiser_and_tag( + 'ai-1234567890', + 'ti-1234567890' + ); + } +} diff --git a/tests/Unit/PinterestForWoocommerceTest.php b/tests/Unit/PinterestForWoocommerceTest.php index 32db0429a..a66cbaaf4 100644 --- a/tests/Unit/PinterestForWoocommerceTest.php +++ b/tests/Unit/PinterestForWoocommerceTest.php @@ -47,7 +47,7 @@ function ( $key ) { }, ARRAY_FILTER_USE_KEY ) - ), + ) ), 'response' => array( 'code' => 200, From db01dad33e95d21dc77620c51c7d044dc1f6845f Mon Sep 17 00:00:00 2001 From: Dima Date: Wed, 31 May 2023 18:48:56 +0300 Subject: [PATCH 09/36] Add Tags rest api endpoint unit tests. Minor code style changes and phpDoc block updates. --- src/API/APIV5.php | 30 ++- src/API/AdvertiserConnect.php | 4 +- src/API/Tags.php | 25 +-- tests/Unit/Api/TagsTest.php | 397 ++++++++++++++++++++++++++++++++++ 4 files changed, 438 insertions(+), 18 deletions(-) create mode 100644 tests/Unit/Api/TagsTest.php diff --git a/src/API/APIV5.php b/src/API/APIV5.php index 8173d970c..ae4dc64d6 100644 --- a/src/API/APIV5.php +++ b/src/API/APIV5.php @@ -113,7 +113,35 @@ public static function get_advertisers( $pinterest_user = null ) { * * @param string $ad_account_id the advertiser_id to request the tags for. * - * @return array + * @return array { + * Tag objects list. + * + * @type array[] $items { + * Tag object. + * + * @type string $ad_account_id Ad account ID. + * @type string $code_snippet Tag code snippet. + * @type ?string $enhanced_match_status The enhanced match status of the tag. + * @type string $id Tag ID. + * @type ?int $last_fired_time_ms Time for the last event fired. + * + * @type string $name Conversion tag name. + * @type string $status Entity status. + * @type string $version Version number. + * @type array $configs { + * Tag Enhanced Match configuration. + * + * @type ?bool $aem_enabled Whether Automatic Enhanced Match email is enabled. + * @type ?int $md_frequency Metadata ingestion frequency. + * @type ?bool $aem_fnln_enabled Whether Automatic Enhanced Match first name and last name is enabled. + * @type ?bool $aem_ph_enabled Whether Automatic Enhanced Match phone is enabled. + * @type ?bool $aem_ge_enabled Whether Automatic Enhanced Match gender is enabled. + * @type ?bool $aem_db_enabled Whether Automatic Enhanced Match birthdate is enabled. + * @type ?bool $aem_loc_enabled Whether Automatic Enhanced Match location is enabled. + * } + * } + * } + * * @throws ApiException|Exception */ public static function get_advertiser_tags( $ad_account_id ) { diff --git a/src/API/AdvertiserConnect.php b/src/API/AdvertiserConnect.php index fab0d8257..dc08918d6 100644 --- a/src/API/AdvertiserConnect.php +++ b/src/API/AdvertiserConnect.php @@ -47,7 +47,7 @@ public function __construct() { * * @return array|WP_Error * - * @throws \Exception PHP Exception. + * @throws Exception PHP Exception. */ public function connect_advertiser( WP_REST_Request $request ) { @@ -58,7 +58,7 @@ public function connect_advertiser( WP_REST_Request $request ) { $enable_aem = $request->has_param( 'enable_aem' ) ? $request->get_param( 'enable_aem' ) : false; if ( ! $advertiser_id || ! $tag_id ) { - throw new \Exception( esc_html__( 'Missing advertiser or tag parameters.', 'pinterest-for-woocommerce' ), 400 ); + throw new Exception( esc_html__( 'Missing advertiser or tag parameters.', 'pinterest-for-woocommerce' ), 400 ); } if ( $enable_aem ) { diff --git a/src/API/Tags.php b/src/API/Tags.php index 6be8499ce..7c10260e8 100644 --- a/src/API/Tags.php +++ b/src/API/Tags.php @@ -49,24 +49,22 @@ public function get_tags( WP_REST_Request $request ) { if ( ! $ad_account_id ) { throw new Exception( esc_html__( 'Advertiser missing', 'pinterest-for-woocommerce' ), 400 ); } + try { $tags = APIV5::get_advertiser_tags( $ad_account_id ); } catch ( Throwable $th ) { throw new Exception( esc_html__( 'Response error', 'pinterest-for-woocommerce' ), 400 ); } - /*$tags = (array) $response['data']; - + $tags = $tags['items'] ?? array(); if ( empty( $tags ) ) { - // No tag created yet. Lets create one. - $tag = Base::create_tag( $ad_account_id ); - - if ( 'success' === $tag['status'] ) { - $tags[ $tag['data']->id ] = $tag['data']; - } else { - throw new \Exception( esc_html__( 'Could not create a tag. Please check the logs for additional information.', 'pinterest-for-woocommerce' ), 400 ); + try { + $tag = APIV5::create_tag( $ad_account_id ); + $tags = [ $tag ]; + } catch ( Throwable $th ) { + throw new Exception( esc_html__( 'Could not create a tag. Please check the logs for additional information.', 'pinterest-for-woocommerce' ), 400 ); } - }*/ + } return array_map( function( $tag ) { @@ -75,15 +73,12 @@ function( $tag ) { 'name' => $tag['name'], ); }, - $tags['items'] ?? array() + $tags ); - } catch ( \Throwable $th ) { - + } catch ( Throwable $th ) { /* Translators: The error description as returned from the API */ $error_message = sprintf( esc_html__( 'No tracking tag available. [%s]', 'pinterest-for-woocommerce' ), $th->getMessage() ); - return new \WP_Error( \PINTEREST_FOR_WOOCOMMERCE_PREFIX . '_tags_error', $error_message, array( 'status' => $th->getCode() ) ); - } } } diff --git a/tests/Unit/Api/TagsTest.php b/tests/Unit/Api/TagsTest.php new file mode 100644 index 000000000..37a8cbad5 --- /dev/null +++ b/tests/Unit/Api/TagsTest.php @@ -0,0 +1,397 @@ +get_routes(); + $this->assertArrayHasKey( '/pinterest/v1/tags', $routes ); + } + + /** + * Tests if the tags endpoint rejects access. + * + * @return void + */ + public function test_tags_endpoint_rejects_access() { + // 1. No authentication. + $request = new WP_REST_Request( 'GET', '/pinterest/v1/tags' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 401, $response->get_status() ); + + // 2. No authorisation. + $user = $this->factory->user->create( array( 'role' => 'guest' ) ); + wp_set_current_user( $user ); + + $request = new WP_REST_Request( 'GET', '/pinterest/v1/tags' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 403, $response->get_status() ); + } + + /** + * Tests if the tags endpoint returns an error when the advertiser is missing. + * + * @return void + */ + public function test_tags_endpoint_returns_advertiser_missing() { + $user = $this->factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $user ); + + $request = new WP_REST_Request( 'GET', '/pinterest/v1/tags' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 400, $response->get_status() ); + $this->assertEquals( + 'pinterest-for-woocommerce_tags_error', + $response->get_data()['code'] ?? '' + ); + $this->assertEquals( + 'No tracking tag available. [Advertiser missing]', + $response->get_data()['message'] ?? '' + ); + } + + /** + * Tests if the tags endpoint returns a list of tags available. + * + * @return void + */ + public function test_tags_endpoint_returns_tags_list() { + $user = $this->factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $user ); + + $enhanced_match = array( + 'aem_enabled' => false, + 'md_frequency' => 1, + 'aem_fnln_enabled' => false, + 'aem_ph_enabled' => false, + 'aem_ge_enabled' => false, + 'aem_db_enabled' => false, + 'aem_loc_enabled' => false, + ); + + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) use ( $enhanced_match) { + return array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + 'items' => array( + array( + 'ad_account_id' => 'ai-123456789', + 'code_snippet' => '', + 'enhanced_match_status' => 'VALIDATION_COMPLETE', + 'id' => 'tag-id-1', + 'last_fired_time_ms' => 0, + 'name' => 'Test Tag 1', + 'status' => 'ACTIVE', + 'version' => 2, + 'configs' => $enhanced_match, + ), + array( + 'ad_account_id' => 'ai-123456789', + 'code_snippet' => '', + 'enhanced_match_status' => 'VALIDATION_COMPLETE', + 'id' => 'tag-id-2', + 'last_fired_time_ms' => 0, + 'name' => 'Test Tag 2', + 'status' => 'ACTIVE', + 'version' => 2, + 'configs' => $enhanced_match, + ), + array( + 'ad_account_id' => 'ai-123456789', + 'code_snippet' => '', + 'enhanced_match_status' => 'VALIDATION_COMPLETE', + 'id' => 'tag-id-3', + 'last_fired_time_ms' => 0, + 'name' => 'Test Tag 3', + 'status' => 'ACTIVE', + 'version' => 2, + 'configs' => $enhanced_match, + ), + array( + 'ad_account_id' => 'ai-123456789', + 'code_snippet' => '', + 'enhanced_match_status' => 'VALIDATION_COMPLETE', + 'id' => 'tag-id-4', + 'last_fired_time_ms' => 0, + 'name' => 'Test Tag 4', + 'status' => 'ACTIVE', + 'version' => 2, + 'configs' => $enhanced_match, + ), + ), + ) + ), + 'response' => array( + 'code' => 200, + ), + 'cookies' => array(), + 'filename' => '', + 'http_response' => new WP_HTTP_Requests_Response( + new Response(), + '' + ), + ); + }, + 10, + 3 + ); + + $request = new WP_REST_Request( 'GET', '/pinterest/v1/tags' ); + $request->set_query_params( + array( + 'advrtsr_id' => 'ai-123456789', + ) + ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( + array( + array( + 'id' => 'tag-id-1', + 'name' => 'Test Tag 1', + ), + array( + 'id' => 'tag-id-2', + 'name' => 'Test Tag 2', + ), + array( + 'id' => 'tag-id-3', + 'name' => 'Test Tag 3', + ), + array( + 'id' => 'tag-id-4', + 'name' => 'Test Tag 4', + ), + ), + $response->get_data() + ); + } + + /** + * Tests if the tags endpoint returns a newly created tag. + * + * @return void + */ + public function test_tags_endpoint_returns_newly_created_tag() { + $user = $this->factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $user ); + + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) { + $body = array(); + + // 1. GET tags list. + if ( 'GET' === $parsed_args['method'] ) { + $body = array( + 'items' => array(), + ); + } + + // 2. POST new tag. + if ( 'POST' === $parsed_args['method'] ) { + $body = array( + 'ad_account_id' => 'ai-123456789', + 'code_snippet' => '', + 'enhanced_match_status' => 'VALIDATION_COMPLETE', + 'id' => 'tag-id-new', + 'last_fired_time_ms' => 0, + 'name' => 'Test Tag New', + 'status' => 'ACTIVE', + 'version' => 2, + 'configs' => array( + 'aem_enabled' => false, + 'md_frequency' => 1, + 'aem_fnln_enabled' => false, + 'aem_ph_enabled' => false, + 'aem_ge_enabled' => false, + 'aem_db_enabled' => false, + 'aem_loc_enabled' => false, + ), + ); + } + + return array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( $body ), + 'response' => array( + 'code' => 200, + ), + 'cookies' => array(), + 'filename' => '', + 'http_response' => new WP_HTTP_Requests_Response( + new Response(), + '' + ), + ); + }, + 10, + 3 + ); + + $request = new WP_REST_Request( 'GET', '/pinterest/v1/tags' ); + $request->set_query_params( + array( + 'advrtsr_id' => 'ai-123456789', + ) + ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( + array( + array( + 'id' => 'tag-id-new', + 'name' => 'Test Tag New', + ) + ), + $response->get_data() + ); + } + + /** + * Tests if the tags endpoint returns a tags list error. + * + * @return void + */ + public function test_tags_endpoint_returns_tags_list_error() { + $user = $this->factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $user ); + + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) { + return array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + 'code' => 0, + 'message' => 'string', + ) + ), + 'response' => array( + 'code' => 500, + ), + 'cookies' => array(), + 'filename' => '', + 'http_response' => new WP_HTTP_Requests_Response( + new Response(), + '' + ), + ); + }, + 10, + 3 + ); + + $request = new WP_REST_Request( 'GET', '/pinterest/v1/tags' ); + $request->set_query_params( + array( + 'advrtsr_id' => 'ai-123456789', + ) + ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 400, $response->get_status() ); + $this->assertEquals( + 'No tracking tag available. [Response error]', + $response->get_data()['message'] ?? '' + ); + } + + /** + * Tests if the tags endpoint returns a tag create error. + * + * @return void + */ + public function test_tags_endpoint_returns_tag_create_error() { + $user = $this->factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $user ); + + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) { + $body = array(); + $code = 200; + + // 1. GET tags list. + if ( 'GET' === $parsed_args['method'] ) { + $body = array( + 'items' => array(), + ); + } + + // 2. POST new tag. + if ( 'POST' === $parsed_args['method'] ) { + $code = 500; + $body = array( + 'code' => 0, + 'message' => 'string', + ); + } + + return array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( $body ), + 'response' => array( + 'code' => $code, + ), + 'cookies' => array(), + 'filename' => '', + 'http_response' => new WP_HTTP_Requests_Response( + new Response(), + '' + ), + ); + }, + 10, + 3 + ); + + $request = new WP_REST_Request( 'GET', '/pinterest/v1/tags' ); + $request->set_query_params( + array( + 'advrtsr_id' => 'ai-123456789', + ) + ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 400, $response->get_status() ); + $this->assertEquals( + 'No tracking tag available. [Could not create a tag. Please check the logs for additional information.]', + $response->get_data()['message'] ?? '' + ); + } +} From ada4580f972c92a5e1eea2388d6725fa9c0488e0 Mon Sep 17 00:00:00 2001 From: Dima Date: Wed, 31 May 2023 23:04:26 +0300 Subject: [PATCH 10/36] Replace api Option class with Settings class to represent the endpoint name. Add settings api endpoint tests. --- class-pinterest-for-woocommerce.php | 2 +- src/API/{Options.php => Settings.php} | 6 +- tests/Unit/Api/SettingsTest.php | 169 ++++++++++++++++++++++++++ tests/Unit/Api/TagsTest.php | 2 +- 4 files changed, 174 insertions(+), 5 deletions(-) rename src/API/{Options.php => Settings.php} (93%) create mode 100644 tests/Unit/Api/SettingsTest.php diff --git a/class-pinterest-for-woocommerce.php b/class-pinterest-for-woocommerce.php index be6725950..c0cfb4e47 100644 --- a/class-pinterest-for-woocommerce.php +++ b/class-pinterest-for-woocommerce.php @@ -589,7 +589,7 @@ public function init_api_endpoints() { new Pinterest\API\FeedIssues(); new Pinterest\API\Tags(); new Pinterest\API\HealthCheck(); - new Pinterest\API\Options(); + new Pinterest\API\Settings(); new Pinterest\API\SyncSettings(); new Pinterest\API\UserInteraction(); } diff --git a/src/API/Options.php b/src/API/Settings.php similarity index 93% rename from src/API/Options.php rename to src/API/Settings.php index cc404a0ea..b2883930a 100644 --- a/src/API/Options.php +++ b/src/API/Settings.php @@ -18,9 +18,9 @@ } /** - * Endpoint handling Options. + * Endpoint handling settings updates. */ -class Options extends VendorAPI { +class Settings extends VendorAPI { /** * Initialize class @@ -47,7 +47,7 @@ public function get_settings() { Pinterest_For_Woocommerce()::maybe_check_billing_setup(); $settings = Pinterest_For_Woocommerce()::get_settings( true ); if ( empty( $settings['account_data']['id'] ) ) { - $integration_data = \Pinterest_For_Woocommerce::get_data( 'integration_data' ); + $integration_data = Pinterest_For_Woocommerce::get_data( 'integration_data' ); $settings['account_data']['id'] = $integration_data['connected_user_id'] ?? ''; } return array( diff --git a/tests/Unit/Api/SettingsTest.php b/tests/Unit/Api/SettingsTest.php new file mode 100644 index 000000000..0e17e0398 --- /dev/null +++ b/tests/Unit/Api/SettingsTest.php @@ -0,0 +1,169 @@ +get_routes(); + $this->assertArrayHasKey( '/pinterest/v1/settings', $routes ); + } + + /** + * Tests if the get/set settings endpoints reject access. + * + * @return void + */ + public function test_settings_endpoint_rejects_access() { + // 1. No authentication. + $request = new WP_REST_Request( 'GET', '/pinterest/v1/settings' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 401, $response->get_status() ); + + $request = new WP_REST_Request( 'POST', '/pinterest/v1/settings' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 401, $response->get_status() ); + + // 2. No authorisation. + $user = $this->factory->user->create( array( 'role' => 'guest' ) ); + wp_set_current_user( $user ); + + $request = new WP_REST_Request( 'GET', '/pinterest/v1/settings' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 403, $response->get_status() ); + + $request = new WP_REST_Request( 'POST', '/pinterest/v1/settings' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 403, $response->get_status() ); + } + + /** + * Tests get settings endpoint returns settings stored inside wp_options. + * + * @return void + */ + public function test_get_settings_endpoint_returns_settings_stored_inside_options() { + $user = $this->factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $user ); + + Pinterest_For_Woocommerce::save_settings( + array ( + 'automatic_enhanced_match_support' => true, + 'last_synced_settings' => '31 May 2023, 03:52:53 pm', + 'track_conversions' => true, + 'enhanced_match_support' => true, + 'save_to_pinterest' => true, + 'rich_pins_on_posts' => true, + 'rich_pins_on_products' => true, + 'product_sync_enabled' => true, + 'enable_debug_logging' => true, + 'erase_plugin_data' => false, + 'ads_campaign_is_active' => true, + 'did_redirect_to_onboarding' => false, + 'account_data' => array ( + 'username' => 'dmytromaksiuta1', + 'full_name' => '', + 'id' => '8842280425079444297', + 'image_medium_url' => 'https://i.pinimg.com/600x600_R/42/f5/36/42f5364f737aff4749a8e9046510828f.jpg', + 'is_partner' => true, + 'verified_user_websites' => array ( 'pinterest.dima.works', 'wordpress.dima.works' ), + 'is_any_website_verified' => true, + 'is_billing_setup' => false, + 'coupon_redeem_info' => array ( + 'redeem_status' => false, + ), + 'available_discounts' => false, + ), + 'tracking_advertiser' => '549765662491', + 'tracking_tag' => '2613286171854', + ) + ); + + $request = new WP_REST_Request( 'GET', '/pinterest/v1/settings' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( + array( + 'pinterest_for_woocommerce' => Pinterest_For_Woocommerce::get_settings( true ), + ), + $response->get_data() + ); + } + + /** + * Tests set settings endpoint successfully sets settings. + * + * @return void + */ + public function test_set_settings_endpoint_successfully_sets_settings() { + $user = $this->factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $user ); + + Pinterest_For_Woocommerce::save_settings( array() ); + + $request = new WP_REST_Request( 'POST', '/pinterest/v1/settings' ); + $request->set_body_params( + array( + 'pinterest_for_woocommerce' => array( + 'automatic_enhanced_match_support' => true, + 'last_synced_settings' => '31 May 2023, 03:52:53 pm', + 'track_conversions' => true, + 'enhanced_match_support' => true, + 'save_to_pinterest' => true, + 'rich_pins_on_posts' => true, + 'rich_pins_on_products' => true, + 'product_sync_enabled' => true, + 'enable_debug_logging' => true, + 'erase_plugin_data' => false, + 'ads_campaign_is_active' => true, + 'did_redirect_to_onboarding' => false, + 'account_data' => array( + 'username' => 'dmytromaksiuta1', + 'full_name' => '', + 'id' => '8842280425079444297', + 'image_medium_url' => 'https://i.pinimg.com/600x600_R/42/f5/36/42f5364f737aff4749a8e9046510828f.jpg', + 'is_partner' => true, + 'verified_user_websites' => array( 'pinterest.dima.works', 'wordpress.dima.works' ), + 'is_any_website_verified' => true, + 'is_billing_setup' => false, + 'coupon_redeem_info' => array( + 'redeem_status' => false, + ), + 'available_discounts' => false, + ), + 'tracking_advertiser' => '549765662491', + 'tracking_tag' => '2613286171854', + ), + ) + ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( + array( + 'pinterest_for_woocommerce' => true, + ), + $response->get_data() + ); + } +} diff --git a/tests/Unit/Api/TagsTest.php b/tests/Unit/Api/TagsTest.php index 37a8cbad5..f64b27bd5 100644 --- a/tests/Unit/Api/TagsTest.php +++ b/tests/Unit/Api/TagsTest.php @@ -20,7 +20,7 @@ class TagsTest extends WP_Test_REST_TestCase { * * @return void */ - public function test_tags_route() { + public function test_tags_route_registered() { $routes = rest_get_server()->get_routes(); $this->assertArrayHasKey( '/pinterest/v1/tags', $routes ); } From f5daf25e8c5dcb179a37f77775cd71ba33aae6ef Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 1 Jun 2023 14:30:26 +0300 Subject: [PATCH 11/36] Add set settings endpoint error tests. --- tests/Unit/Api/SettingsTest.php | 89 +++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/tests/Unit/Api/SettingsTest.php b/tests/Unit/Api/SettingsTest.php index 0e17e0398..2fe159d98 100644 --- a/tests/Unit/Api/SettingsTest.php +++ b/tests/Unit/Api/SettingsTest.php @@ -166,4 +166,93 @@ public function test_set_settings_endpoint_successfully_sets_settings() { $response->get_data() ); } + + /** + * Tests set settings endpoint returns error if wrong data key is passed. + * + * @return void + */ + public function test_set_settings_endpoint_returns_error_if_data_is_missing() { + $user = $this->factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $user ); + + Pinterest_For_Woocommerce::save_settings( array() ); + + $request = new WP_REST_Request( 'POST', '/pinterest/v1/settings' ); + $request->set_body_params( + array( + 'some_other_parameter_name' => array(), + ) + ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 400, $response->get_status() ); + $this->assertEquals( + 'Missing option parameters.', + $response->get_data()['message'] ?? '' + ); + } + + /** + * Tests set settings endpoint returns error if save fails. + * + * @return void + */ + public function test_set_settings_endpoint_returns_error_if_save_fails() { + $user = $this->factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $user ); + + // Make update_option fail. + add_filter( + 'pre_update_option', + function () { + return false; + } + ); + + Pinterest_For_Woocommerce::save_settings( array() ); + + $request = new WP_REST_Request( 'POST', '/pinterest/v1/settings' ); + $request->set_body_params( + array( + 'pinterest_for_woocommerce' => array( + 'automatic_enhanced_match_support' => true, + 'last_synced_settings' => '31 May 2023, 03:52:53 pm', + 'track_conversions' => true, + 'enhanced_match_support' => true, + 'save_to_pinterest' => true, + 'rich_pins_on_posts' => true, + 'rich_pins_on_products' => true, + 'product_sync_enabled' => true, + 'enable_debug_logging' => true, + 'erase_plugin_data' => false, + 'ads_campaign_is_active' => true, + 'did_redirect_to_onboarding' => false, + 'account_data' => array( + 'username' => 'dmytromaksiuta1', + 'full_name' => '', + 'id' => '8842280425079444297', + 'image_medium_url' => 'https://i.pinimg.com/600x600_R/42/f5/36/42f5364f737aff4749a8e9046510828f.jpg', + 'is_partner' => true, + 'verified_user_websites' => array( 'pinterest.dima.works', 'wordpress.dima.works' ), + 'is_any_website_verified' => true, + 'is_billing_setup' => false, + 'coupon_redeem_info' => array( + 'redeem_status' => false, + ), + 'available_discounts' => false, + ), + 'tracking_advertiser' => '549765662491', + 'tracking_tag' => '2613286171854', + ), + ) + ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 500, $response->get_status() ); + $this->assertEquals( + 'There was an error saving the settings.', + $response->get_data()['message'] ?? '' + ); + } } From 8ab609d6c4f5ecd331e9feb0598ff9d34d4c5363 Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 1 Jun 2023 17:57:27 +0300 Subject: [PATCH 12/36] Rename health check into just health to be the same as the endpoint path. Add health test file. Minor code changes, mostly namespace related. --- class-pinterest-for-woocommerce.php | 21 ++------ src/API/APIV5.php | 20 ++++---- src/API/AdvertiserConnect.php | 7 ++- src/API/Advertisers.php | 23 +++------ src/API/Auth.php | 3 +- src/API/{HealthCheck.php => Health.php} | 6 +-- tests/Unit/Api/HealthTest.php | 68 +++++++++++++++++++++++++ 7 files changed, 99 insertions(+), 49 deletions(-) rename src/API/{HealthCheck.php => Health.php} (96%) create mode 100644 tests/Unit/Api/HealthTest.php diff --git a/class-pinterest-for-woocommerce.php b/class-pinterest-for-woocommerce.php index c0cfb4e47..6fd698b80 100644 --- a/class-pinterest-for-woocommerce.php +++ b/class-pinterest-for-woocommerce.php @@ -588,7 +588,7 @@ public function init_api_endpoints() { new Pinterest\API\FeedState(); new Pinterest\API\FeedIssues(); new Pinterest\API\Tags(); - new Pinterest\API\HealthCheck(); + new Pinterest\API\Health(); new Pinterest\API\Settings(); new Pinterest\API\SyncSettings(); new Pinterest\API\UserInteraction(); @@ -692,7 +692,7 @@ public static function disconnect() { self::flush_options(); // At this point we're disconnected. return true; - } catch ( \Exception $th ) { + } catch ( Exception $th ) { // There was an error disconnecting merchant. return false; } @@ -947,12 +947,11 @@ public static function update_commerce_integration( string $external_business_id public static function delete_commerce_integration(): bool { $external_business_id = Pinterest_For_Woocommerce::get_data( 'integration_data' )['external_business_id']; - $response = Pinterest\API\APIV5::make_request( + Pinterest\API\APIV5::make_request( "integrations/commerce/{$external_business_id}", 'DELETE' ); - // @TODO: add proper response handling. Check for success http status 204 or unexpected error in return. return true; } @@ -996,20 +995,6 @@ public static function update_account_data() { try { $integration_data = self::get_data( 'integration_data' ); $account_data = Pinterest\API\APIV5::get_account_info(); - /* - Example of $account_data - array ( - 'monthly_views' => -1, - 'following_count' => 0, - 'account_type' => 'BUSINESS', - 'website_url' => 'http://pinterest.dima.works', - 'username' => 'dmytromaksiuta1', - 'board_count' => 0, - 'pin_count' => -20, - 'profile_image' => 'https://i.pinimg.com/600x600_R/42/f5/36/42f5364f737aff4749a8e9046510828f.jpg', - 'follower_count' => 1, - 'business_name' => 'WooCommerce', - )*/ $data = array( 'username' => $account_data['username'] ?? '', diff --git a/src/API/APIV5.php b/src/API/APIV5.php index ae4dc64d6..08cc9a1eb 100644 --- a/src/API/APIV5.php +++ b/src/API/APIV5.php @@ -186,15 +186,17 @@ public static function create_tag( $ad_account_id ) { return self::make_request( "ad_accounts/{$ad_account_id}/conversion_tags", 'POST', - array( - 'name' => $tag_name, - 'aem_enabled' => true, - 'md_frequency' => 1, - 'aem_fnln_enabled' => true, - 'aem_ph_enabled' => true, - 'aem_ge_enabled' => true, - 'aem_db_enabled' => true, - 'ae_loc_enabled' => true, + json_encode( + array( + 'name' => $tag_name, + 'aem_enabled' => true, + 'md_frequency' => 1, + 'aem_fnln_enabled' => true, + 'aem_ph_enabled' => true, + 'aem_ge_enabled' => true, + 'aem_db_enabled' => true, + 'ae_loc_enabled' => true, + ) ) ); } diff --git a/src/API/AdvertiserConnect.php b/src/API/AdvertiserConnect.php index dc08918d6..669fe2c92 100644 --- a/src/API/AdvertiserConnect.php +++ b/src/API/AdvertiserConnect.php @@ -61,6 +61,7 @@ public function connect_advertiser( WP_REST_Request $request ) { throw new Exception( esc_html__( 'Missing advertiser or tag parameters.', 'pinterest-for-woocommerce' ), 400 ); } + // Automatic Enhanced Match is enabled by default (at least Pinterest said so). if ( $enable_aem ) { // @TODO: We do not have an API v5 analog for this call. Commenting it out temporarily. #self::enable_aem_tag( $tag_id ); @@ -79,7 +80,7 @@ public function connect_advertiser( WP_REST_Request $request ) { // Update integration with new advertiser and a tag. return self::connect_advertiser_and_tag( $advertiser_id, $tag_id ); - } catch ( \Throwable $th ) { + } catch ( Throwable $th ) { /* Translators: The error description as returned from the API */ $error_message = sprintf( esc_html__( 'Could not connect advertiser with Pinterest. [%s]', 'pinterest-for-woocommerce' ), $th->getMessage() ); @@ -105,7 +106,9 @@ public function connect_advertiser( WP_REST_Request $request ) { */ public static function connect_advertiser_and_tag( string $advertiser_id, string $tag_id ): array { - $external_business_id = Pinterest_For_Woocommerce::get_data( 'external_business_id' ); + $integration_data = Pinterest_For_Woocommerce::get_data( 'integration_data' ); + $external_business_id = $integration_data['external_business_id'] ?? ''; + $data = array( 'connected_advertiser_id' => $advertiser_id, 'connected_tag_id' => $tag_id, diff --git a/src/API/Advertisers.php b/src/API/Advertisers.php index 358491be1..12ccfe568 100644 --- a/src/API/Advertisers.php +++ b/src/API/Advertisers.php @@ -8,6 +8,9 @@ namespace Automattic\WooCommerce\Pinterest\API; +use Exception; +use Throwable; +use WP_Error; use \WP_REST_Server; use \WP_REST_Request; @@ -40,25 +43,13 @@ public function __construct() { * * @return array|WP_Error * - * @throws \Exception PHP Exception. + * @throws Exception PHP Exception. */ public function get_advertisers( WP_REST_Request $request ) { - try { - - $advertisers = APIV5::get_advertisers(); - + $advertisers = APIV5::get_advertisers(); $terms_agreed = $request->has_param( 'terms_agreed' ) ? (int) $request->get_param( 'terms_agreed' ) : false; - /*if ( 'success' !== $advertisers['status'] && 1000 === $advertisers['code'] ) { - // User needs to take manual action in Pinterest dashboard. - throw new \Exception( esc_html__( 'No advertiser exists.', 'pinterest-for-woocommerce' ), 1000 ); - } - - if ( 'success' !== $advertisers['status'] ) { - throw new \Exception( esc_html__( 'Response error', 'pinterest-for-woocommerce' ), 400 ); - }*/ - if ( empty( $advertisers['items'] ) && ! empty( $terms_agreed ) ) { $advertiser = Base::create_advertiser( $terms_agreed ); $advertisers['data'] = 'success' === $advertiser['status'] ? array( $advertiser['data'] ) : array(); @@ -75,10 +66,10 @@ function ( $item ) { $advertisers['items'] ), ); - } catch ( \Throwable $th ) { + } catch ( Throwable $th ) { /* Translators: The error description as returned from the API */ $error_message = sprintf( esc_html__( 'Could not fetch advertisers for Pinterest account ID. [%s]', 'pinterest-for-woocommerce' ), $th->getMessage() ); - return new \WP_Error( \PINTEREST_FOR_WOOCOMMERCE_PREFIX . '_advertisers_error', $error_message, array( 'status' => $th->getCode() ) ); + return new WP_Error( \PINTEREST_FOR_WOOCOMMERCE_PREFIX . '_advertisers_error', $error_message, array( 'status' => $th->getCode() ) ); } } } diff --git a/src/API/Auth.php b/src/API/Auth.php index 03eacf2cb..565f7ac3f 100644 --- a/src/API/Auth.php +++ b/src/API/Auth.php @@ -10,7 +10,8 @@ use Automattic\WooCommerce\Pinterest\Logger as Logger; use Throwable; -use \WP_REST_Request; +use WP_HTTP_Response; +use WP_REST_Request; if ( ! defined( 'ABSPATH' ) ) { exit; diff --git a/src/API/HealthCheck.php b/src/API/Health.php similarity index 96% rename from src/API/HealthCheck.php rename to src/API/Health.php index 03ad297eb..5fc4e446d 100644 --- a/src/API/HealthCheck.php +++ b/src/API/Health.php @@ -9,7 +9,7 @@ namespace Automattic\WooCommerce\Pinterest\API; use Automattic\WooCommerce\Pinterest as Pinterest; -use \WP_REST_Server; +use WP_REST_Server; if ( ! defined( 'ABSPATH' ) ) { exit; @@ -18,7 +18,7 @@ /** * Endpoint used to check the Health status of the connected Merchant object. */ -class HealthCheck extends VendorAPI { +class Health extends VendorAPI { /** * Initialize class @@ -77,7 +77,7 @@ public function health_check() { return $response; - } catch ( \Throwable $th ) { + } catch ( Throwable $th ) { /* Translators: The error description as returned from the API */ $error_message = sprintf( __( 'Could not fetch account status. [%s]', 'pinterest-for-woocommerce' ), $th->getMessage() ); diff --git a/tests/Unit/Api/HealthTest.php b/tests/Unit/Api/HealthTest.php new file mode 100644 index 000000000..a6fcddaad --- /dev/null +++ b/tests/Unit/Api/HealthTest.php @@ -0,0 +1,68 @@ +get_routes(); + $this->assertArrayHasKey( '/pinterest/v1/health', $routes ); + } + + /** + * Tests if the health endpoint rejects access. + * + * @return void + */ + public function test_tags_endpoint_rejects_access() { + // 1. No authentication. + $request = new WP_REST_Request( 'GET', '/pinterest/v1/health' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 401, $response->get_status() ); + + // 2. No authorisation. + $user = $this->factory->user->create( array( 'role' => 'guest' ) ); + wp_set_current_user( $user ); + + $request = new WP_REST_Request( 'GET', '/pinterest/v1/health' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 403, $response->get_status() ); + } + + /** + * Tests if the health endpoint returns health status. + * + * @return void + */ + public function test_tags_endpoint_returns_advertiser_missing() { + $user = $this->factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $user ); + + $request = new WP_REST_Request( 'GET', '/pinterest/v1/health' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( + array( + 'status' => 'approved', + ), + $response->get_data() + ); + } +} From 86d97e51089b6d72157c4bafc4464ea4ee0c8905 Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 1 Jun 2023 18:21:19 +0300 Subject: [PATCH 13/36] Working on code styling. --- src/API/APIV5.php | 156 ++++++++++++++++++---------------- src/API/AdvertiserConnect.php | 10 +-- 2 files changed, 88 insertions(+), 78 deletions(-) diff --git a/src/API/APIV5.php b/src/API/APIV5.php index 08cc9a1eb..1646fb291 100644 --- a/src/API/APIV5.php +++ b/src/API/APIV5.php @@ -53,20 +53,24 @@ public static function prepare_request( $endpoint, $method = 'POST', $payload = * * @since x.x.x * - * @return array|mixed + * @return mixed|array { + * User account object. + * + * @type string $account_type Type of account. Enum: "PINNER" "BUSINESS". + * @type string $profile_image + * @type string $website_url + * @type string $username + * @type string $business_name + * @type int $board_count User account board count. + * @type int $pin_count User account pin count. This includes both created and saved pins. + * @type int $follower_count User account follower count. + * @type int $following_count User account following count. + * @type int $monthly_views User account monthly views. + * } * @throws ApiException */ public static function get_account_info() { - $integration_data = \Pinterest_For_Woocommerce::get_data( 'integration_data', true ); - return self::make_request( - 'user_account', - 'GET'/*, - json_encode( - array( - 'ad_account_id' => $integration_data['connected_advertiser_id'] ?? '', - ) - )*/ - ); + return self::make_request( 'user_account', 'GET' ); } @@ -75,14 +79,20 @@ public static function get_account_info() { * * @since x.x.x * - * @return array + * @return array { + * User account websites. + * + * @type array[] $items { + * @type string $website Website with path or domain only + * @type string $status Status of the verification process + * @type string $verified_at UTC timestamp when the verification happened - sometimes missing + * } + * @type string $bookmark + * } * @throws ApiException */ public static function get_user_websites() { - return self::make_request( - 'user_account/websites', - 'GET' - ); + return self::make_request( 'user_account/websites', 'GET' ); } /** @@ -114,32 +124,32 @@ public static function get_advertisers( $pinterest_user = null ) { * @param string $ad_account_id the advertiser_id to request the tags for. * * @return array { - * Tag objects list. - * - * @type array[] $items { - * Tag object. - * - * @type string $ad_account_id Ad account ID. - * @type string $code_snippet Tag code snippet. - * @type ?string $enhanced_match_status The enhanced match status of the tag. - * @type string $id Tag ID. - * @type ?int $last_fired_time_ms Time for the last event fired. - * - * @type string $name Conversion tag name. - * @type string $status Entity status. - * @type string $version Version number. - * @type array $configs { - * Tag Enhanced Match configuration. - * - * @type ?bool $aem_enabled Whether Automatic Enhanced Match email is enabled. - * @type ?int $md_frequency Metadata ingestion frequency. - * @type ?bool $aem_fnln_enabled Whether Automatic Enhanced Match first name and last name is enabled. - * @type ?bool $aem_ph_enabled Whether Automatic Enhanced Match phone is enabled. - * @type ?bool $aem_ge_enabled Whether Automatic Enhanced Match gender is enabled. - * @type ?bool $aem_db_enabled Whether Automatic Enhanced Match birthdate is enabled. - * @type ?bool $aem_loc_enabled Whether Automatic Enhanced Match location is enabled. - * } - * } + * Tag objects list. + * + * @type array[] $items { + * Tag object. + * + * @type string $ad_account_id Ad account ID. + * @type string $code_snippet Tag code snippet. + * @type ?string $enhanced_match_status The enhanced match status of the tag. + * @type string $id Tag ID. + * @type ?int $last_fired_time_ms Time for the last event fired. + * + * @type string $name Conversion tag name. + * @type string $status Entity status. + * @type string $version Version number. + * @type array $configs { + * Tag Enhanced Match configuration. + * + * @type ?bool $aem_enabled Whether Automatic Enhanced Match email is enabled. + * @type ?int $md_frequency Metadata ingestion frequency. + * @type ?bool $aem_fnln_enabled Whether Automatic Enhanced Match first name and last name is enabled. + * @type ?bool $aem_ph_enabled Whether Automatic Enhanced Match phone is enabled. + * @type ?bool $aem_ge_enabled Whether Automatic Enhanced Match gender is enabled. + * @type ?bool $aem_db_enabled Whether Automatic Enhanced Match birthdate is enabled. + * @type ?bool $aem_loc_enabled Whether Automatic Enhanced Match location is enabled. + * } + * } * } * * @throws ApiException|Exception @@ -157,27 +167,27 @@ public static function get_advertiser_tags( $ad_account_id ) { * @param string $ad_account_id the advertiser_id to create a tag for. * * @return array { - * Tag object. - * - * @type string $ad_account_id Ad account ID. - * @type string $code_snippet Tag code snippet. - * @type ?string $enhanced_match_status The enhanced match status of the tag. - * @type string $id Tag ID. - * @type ?int $last_fired_time_ms Time for the last event fired. - * @type string $name Conversion tag name. - * @type string $status Entity status. - * @type string $version Version number. - * @type array $configs { - * Tag configuration. - * - * @type ?bool $aem_enabled Whether Automatic Enhanced Match email is enabled. - * @type ?int $md_frequency Metadata ingestion frequency. - * @type ?bool $aem_fnln_enabled Whether Automatic Enhanced Match name is enabled. - * @type ?bool $aem_ph_enabled Whether Automatic Enhanced Match phone is enabled. - * @type ?bool $aem_ge_enabled Whether Automatic Enhanced Match gender is enabled. - * @type ?bool $aem_db_enabled Whether Automatic Enhanced Match birthdate is enabled. - * @type ?bool $aem_loc_enabled Whether Automatic Enhanced Match location is enabled. - * } + * Tag object. + * + * @type string $ad_account_id Ad account ID. + * @type string $code_snippet Tag code snippet. + * @type ?string $enhanced_match_status The enhanced match status of the tag. + * @type string $id Tag ID. + * @type ?int $last_fired_time_ms Time for the last event fired. + * @type string $name Conversion tag name. + * @type string $status Entity status. + * @type string $version Version number. + * @type array $configs { + * Tag configuration. + * + * @type ?bool $aem_enabled Whether Automatic Enhanced Match email is enabled. + * @type ?int $md_frequency Metadata ingestion frequency. + * @type ?bool $aem_fnln_enabled Whether Automatic Enhanced Match name is enabled. + * @type ?bool $aem_ph_enabled Whether Automatic Enhanced Match phone is enabled. + * @type ?bool $aem_ge_enabled Whether Automatic Enhanced Match gender is enabled. + * @type ?bool $aem_db_enabled Whether Automatic Enhanced Match birthdate is enabled. + * @type ?bool $aem_loc_enabled Whether Automatic Enhanced Match location is enabled. + * } * } * @throws ApiException|Exception */ @@ -207,13 +217,13 @@ public static function create_tag( $ad_account_id ) { * @since x.x.x * * @return array { - * Data needed to verify a website. + * Data needed to verify a website. * - * @type string $verification_code Code to check against the user claiming the website. - * @type string $dns_txt_record DNS TXT record to check against for the website to be claimed. - * @type string $metatag META tag the verification process searches for the website to be claimed. - * @type string $filename File expected to find on the website being claimed. - * @type string $file_content A full html file to upload to the website in order for it to be claimed. + * @type string $verification_code Code to check against the user claiming the website. + * @type string $dns_txt_record DNS TXT record to check against for the website to be claimed. + * @type string $metatag META tag the verification process searches for the website to be claimed. + * @type string $filename File expected to find on the website being claimed. + * @type string $file_content A full html file to upload to the website in order for it to be claimed. * } * @throws PinterestApiException */ @@ -228,11 +238,11 @@ public static function domain_verification_data(): array { * * @param string $domain Domain to verify. * @return array { - * Data returned by Pinterest after the verification request. + * Data returned by Pinterest after the verification request. * - * @type string $website Website with path or domain only. - * @type string $status Status of the verification process. - * @type string $verified_at UTC timestamp when the verification happened - sometimes missing. + * @type string $website Website with path or domain only. + * @type string $status Status of the verification process. + * @type string $verified_at UTC timestamp when the verification happened - sometimes missing. * } * @throws PinterestApiException */ diff --git a/src/API/AdvertiserConnect.php b/src/API/AdvertiserConnect.php index 669fe2c92..fab655bdb 100644 --- a/src/API/AdvertiserConnect.php +++ b/src/API/AdvertiserConnect.php @@ -62,10 +62,10 @@ public function connect_advertiser( WP_REST_Request $request ) { } // Automatic Enhanced Match is enabled by default (at least Pinterest said so). - if ( $enable_aem ) { + //if ( $enable_aem ) { // @TODO: We do not have an API v5 analog for this call. Commenting it out temporarily. #self::enable_aem_tag( $tag_id ); - } + //} $is_connected = Pinterest_For_Woocommerce()::get_data( 'is_advertiser_connected' ); @@ -97,10 +97,10 @@ public function connect_advertiser( WP_REST_Request $request ) { * @param string $tag_id The ID of the tag. * * @return array { - * Updates Pinterest integration with the new advertiser and tag. + * Updates Pinterest integration with the new advertiser and tag. * - * @type string $connected The ID of the connected advertiser. - * @type bool $reconnected Whether the advertiser was reconnected. + * @type string $connected The ID of the connected advertiser. + * @type bool $reconnected Whether the advertiser was reconnected. * } * @throws Exception PHP Exception. */ From 7376dd4bc48f44637b04398c25fe54ea5e3bc54b Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 1 Jun 2023 18:44:45 +0300 Subject: [PATCH 14/36] Working on code styling. --- src/API/APIV5.php | 27 +++++++++++++++++++-------- src/API/AdvertiserConnect.php | 6 +++--- src/API/Base.php | 5 +++-- src/API/DomainVerification.php | 2 +- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/API/APIV5.php b/src/API/APIV5.php index 1646fb291..87b50e315 100644 --- a/src/API/APIV5.php +++ b/src/API/APIV5.php @@ -67,7 +67,7 @@ public static function prepare_request( $endpoint, $method = 'POST', $payload = * @type int $following_count User account following count. * @type int $monthly_views User account monthly views. * } - * @throws ApiException + * @throws ApiException Throws 403 and 500 exceptions. */ public static function get_account_info() { return self::make_request( 'user_account', 'GET' ); @@ -89,7 +89,7 @@ public static function get_account_info() { * } * @type string $bookmark * } - * @throws ApiException + * @throws ApiException Throws 403 and 500 exceptions. */ public static function get_user_websites() { return self::make_request( 'user_account/websites', 'GET' ); @@ -100,8 +100,16 @@ public static function get_user_websites() { * * @since x.x.x * - * @return array|mixed - * @throws ApiException + * @return mixed|array[] { + * Linked businesses list. + * + * @type string $username + * @type string $image_small_url + * @type string $image_medium_url + * @type string $image_large_url + * @type string $image_xlarge_url + * } + * @throws ApiException Throws 500 exception in case of unexpected error. */ public static function get_linked_businesses() { return self::make_request( 'user_account/businesses', 'GET' ); @@ -112,6 +120,8 @@ public static function get_linked_businesses() { * * @since x.x.x * + * @param string $pinterest_user The Pinterest User ID. + * * @return mixed */ public static function get_advertisers( $pinterest_user = null ) { @@ -152,7 +162,7 @@ public static function get_advertisers( $pinterest_user = null ) { * } * } * - * @throws ApiException|Exception + * @throws ApiException|Exception Throws 500 exception. */ public static function get_advertiser_tags( $ad_account_id ) { return self::make_request( "ad_accounts/{$ad_account_id}/conversion_tags", 'GET' ); @@ -160,6 +170,7 @@ public static function get_advertiser_tags( $ad_account_id ) { /** * Create a tag for the given advertiser. + * * @link https://developers.pinterest.com/docs/api/v5/#operation/conversion_tags/create * * @since x.x.x @@ -189,7 +200,7 @@ public static function get_advertiser_tags( $ad_account_id ) { * @type ?bool $aem_loc_enabled Whether Automatic Enhanced Match location is enabled. * } * } - * @throws ApiException|Exception + * @throws ApiException|Exception Throws 500 exception. */ public static function create_tag( $ad_account_id ) { $tag_name = self::get_tag_name(); @@ -225,7 +236,7 @@ public static function create_tag( $ad_account_id ) { * @type string $filename File expected to find on the website being claimed. * @type string $file_content A full html file to upload to the website in order for it to be claimed. * } - * @throws PinterestApiException + * @throws PinterestApiException If the request fails with 403 or 500 status. */ public static function domain_verification_data(): array { return self::make_request( 'user_account/websites/verification' ); @@ -244,7 +255,7 @@ public static function domain_verification_data(): array { * @type string $status Status of the verification process. * @type string $verified_at UTC timestamp when the verification happened - sometimes missing. * } - * @throws PinterestApiException + * @throws PinterestApiException If the request fails with 500 status. */ public static function domain_metatag_verification_request( string $domain ): array { return self::make_request( diff --git a/src/API/AdvertiserConnect.php b/src/API/AdvertiserConnect.php index fab655bdb..48fc7667a 100644 --- a/src/API/AdvertiserConnect.php +++ b/src/API/AdvertiserConnect.php @@ -62,10 +62,10 @@ public function connect_advertiser( WP_REST_Request $request ) { } // Automatic Enhanced Match is enabled by default (at least Pinterest said so). - //if ( $enable_aem ) { + // if ( $enable_aem ) { // @TODO: We do not have an API v5 analog for this call. Commenting it out temporarily. - #self::enable_aem_tag( $tag_id ); - //} + # self::enable_aem_tag( $tag_id ); + // } $is_connected = Pinterest_For_Woocommerce()::get_data( 'is_advertiser_connected' ); diff --git a/src/API/Base.php b/src/API/Base.php index 94ef6a23c..b5a4a8325 100644 --- a/src/API/Base.php +++ b/src/API/Base.php @@ -11,6 +11,7 @@ use Automattic\WooCommerce\Pinterest as Pinterest; use Automattic\WooCommerce\Pinterest\Logger as Logger; +use Automattic\WooCommerce\Pinterest\PinterestApiException; use Automattic\WooCommerce\Pinterest\PinterestApiException as ApiException; use \Exception; @@ -77,8 +78,8 @@ public static function instance() { * * @return array * - * @throws ApiException PHP exception. - * @throws Exception PHP exception. + * @throws PinterestApiException Pinterest API exception in case of API error in response. + * @throws Exception PHP exception. */ public static function make_request( $endpoint, $method = 'POST', $payload = array(), $api = '', $cache_expiry = false ) { diff --git a/src/API/DomainVerification.php b/src/API/DomainVerification.php index a55f1b122..20198a94f 100644 --- a/src/API/DomainVerification.php +++ b/src/API/DomainVerification.php @@ -77,7 +77,7 @@ public function maybe_handle_verification() { ) ); } - // return self::trigger_domain_verification(); + // return self::trigger_domain_verification();. } From eedec1d7af589a77b2c3d2020cca46cbe99c63cf Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 1 Jun 2023 19:00:38 +0300 Subject: [PATCH 15/36] Working on code styling. --- class-pinterest-for-woocommerce.php | 144 ++++++++++++++-------------- src/API/AdvertiserConnect.php | 2 +- 2 files changed, 75 insertions(+), 71 deletions(-) diff --git a/class-pinterest-for-woocommerce.php b/class-pinterest-for-woocommerce.php index 6fd698b80..5b50775ca 100644 --- a/class-pinterest-for-woocommerce.php +++ b/class-pinterest-for-woocommerce.php @@ -282,7 +282,7 @@ public function init_plugin() { add_action( 'parse_request', array( $this, 'verification_request' ) ); // Disconnect advertiser if advertiser or tag change. - //add_action( 'update_option_pinterest_for_woocommerce', array( $this, 'maybe_disconnect_advertiser' ), 10, 2 ); + // add_action( 'update_option_pinterest_for_woocommerce', array( $this, 'maybe_disconnect_advertiser' ), 10, 2 );. // Init marketing notifications. add_action( Heartbeat::DAILY, array( $this, 'init_marketing_notifications' ) ); @@ -650,19 +650,19 @@ public static function save_token_data( $token ) { * @since x.x.x * * @param array $connection_info_data The array containing the connection info data. - * @return array + * @return bool True if the data was saved successfully. */ - public static function save_connection_info_data( $connection_info_data ) { + public static function save_connection_info_data( array $connection_info_data ): bool { return self::save_data( 'connection_info_data', $connection_info_data ); } /** * Saves the integration data. * - * @param $integration_data - * @return bool + * @param array $integration_data The array containing the integration data. + * @return bool True if the data was saved successfully. */ - public static function save_integration_data( $integration_data ) { + public static function save_integration_data( array $integration_data ): bool { return self::save_data( 'integration_data', $integration_data ); } @@ -671,11 +671,11 @@ public static function save_integration_data( $integration_data ) { * * @since 1.0.0 * - * @return boolean True if disconnection was successful. + * @return bool True if disconnection was successful. * - * @throws \Exception PHP Exception. + * @throws Exception PHP Exception. */ - public static function disconnect() { + public static function disconnect(): bool { /* * If there is no business connected, disconnecting merchant will throw error. * Just need to clean account data in these cases. @@ -834,28 +834,28 @@ public function maybe_inject_verification_code() { * @since x.x.x * * @return array { - * Integration data returned by Pinterest. - * - * @type string $id ID of the integration (string all digits). - * @type string $external_business_id External business ID for the integration. - * @type string $connected_merchant_id Connected merchant ID for the integration. - * @type string $connected_user_id Connected user ID for the integration. - * @type string $connected_advertiser_id Connected advertiser ID for the integration. - * @type string $connected_lba_id Connected LBA ID for the integration. - * @type string $connected_tag_id Connected tag ID for the integration. - * @type int $partner_access_token_expiry Partner access token expiry for the integration. - * @type int $partner_refresh_token_expiry Partner refresh token expiry for the integration. - * @type string $scopes Scopes for the integration. - * @type int $created_timestamp Created timestamp for the integration. - * @type int $updated_timestamp Updated timestamp for the integration. - * @type string $additional_id_1 Additional ID 1 for the integration. - * @type string $partner_metadata Partner metadata for the integration. + * Integration data returned by Pinterest. + * + * @type string $id ID of the integration (string all digits). + * @type string $external_business_id External business ID for the integration. + * @type string $connected_merchant_id Connected merchant ID for the integration. + * @type string $connected_user_id Connected user ID for the integration. + * @type string $connected_advertiser_id Connected advertiser ID for the integration. + * @type string $connected_lba_id Connected LBA ID for the integration. + * @type string $connected_tag_id Connected tag ID for the integration. + * @type int $partner_access_token_expiry Partner access token expiry for the integration. + * @type int $partner_refresh_token_expiry Partner refresh token expiry for the integration. + * @type string $scopes Scopes for the integration. + * @type int $created_timestamp Created timestamp for the integration. + * @type int $updated_timestamp Updated timestamp for the integration. + * @type string $additional_id_1 Additional ID 1 for the integration. + * @type string $partner_metadata Partner metadata for the integration. * } - * @throws PinterestApiException + * @throws PinterestApiException In case of 404, 409 and 500 errors from Pinterest. */ public static function create_commerce_integration(): array { $external_business_id = self::generate_external_business_id(); - $connection_data = Pinterest_For_Woocommerce::get_data( 'connection_info_data', true ); + $connection_data = self::get_data( 'connection_info_data', true ); $response = Pinterest\API\APIV5::make_request( 'integrations/commerce', @@ -873,12 +873,12 @@ public static function create_commerce_integration(): array { /* * In case of successful response we save our integration data into a database. * Data we save includes but not limited to: - * external business id, - * id, - * connected_user_id, - * etc. + * external business id, + * id, + * connected_user_id, + * etc. */ - Pinterest_For_Woocommerce::save_integration_data( $response ); + self::save_integration_data( $response ); self::save_setting( 'tracking_advertiser', $response['connected_advertiser_id'] ); self::save_setting( 'tracking_tag', $response['connected_tag_id'] ); @@ -891,48 +891,50 @@ public static function create_commerce_integration(): array { * * @since x.x.x * - * @param string $external_business_id - * @param array { - * @type string $external_business_id External business ID for the integration. - * @type string $connected_merchant_id Connected merchant ID for the integration. - * @type string $connected_advertiser_id Connected advertiser ID for the integration. - * @type string $connected_lba_id Connected LBA ID for the integration. - * @type string $connected_tag_id Connected tag ID for the integration. - * @type string $partner_access_token Partner access token for the integration. - * @type string $partner_refresh_token Partner refresh token for the integration. - * @type string $partner_primary_email Partner primary email for the integration. - * @type int $partner_access_token_expiry Partner access token expiry for the integration. - * @type int $partner_refresh_token_expiry Partner refresh token expiry for the integration. - * @type string $scopes Scopes for the integration. - * @type string $additional_id_1 Additional ID 1 for the integration. - * @type string $partner_metadata Partner metadata for the integration. + * @param string $external_business_id External business ID for the integration. + * @param array $data { + * Integration data to update with Pinterest. + * + * @type string $external_business_id External business ID for the integration. + * @type string $connected_merchant_id Connected merchant ID for the integration. + * @type string $connected_advertiser_id Connected advertiser ID for the integration. + * @type string $connected_lba_id Connected LBA ID for the integration. + * @type string $connected_tag_id Connected tag ID for the integration. + * @type string $partner_access_token Partner access token for the integration. + * @type string $partner_refresh_token Partner refresh token for the integration. + * @type string $partner_primary_email Partner primary email for the integration. + * @type int $partner_access_token_expiry Partner access token expiry for the integration. + * @type int $partner_refresh_token_expiry Partner refresh token expiry for the integration. + * @type string $scopes Scopes for the integration. + * @type string $additional_id_1 Additional ID 1 for the integration. + * @type string $partner_metadata Partner metadata for the integration. * } * * @return array { - * Integration data returned by Pinterest. - * - * @type string $id ID of the integration (string all digits). - * @type string $external_business_id External business ID for the integration. - * @type string $connected_merchant_id Connected merchant ID for the integration. - * @type string $connected_user_id Connected user ID for the integration. - * @type string $connected_advertiser_id Connected advertiser ID for the integration. - * @type string $connected_lba_id Connected LBA ID for the integration. - * @type string $connected_tag_id Connected tag ID for the integration. - * @type int $partner_access_token_expiry Partner access token expiry for the integration. - * @type int $partner_refresh_token_expiry Partner refresh token expiry for the integration. - * @type string $scopes Scopes for the integration. - * @type int $created_timestamp Created timestamp for the integration. - * @type int $updated_timestamp Updated timestamp for the integration. - * @type string $additional_id_1 Additional ID 1 for the integration. - * @type string $partner_metadata Partner metadata for the integration. + * Integration data returned by Pinterest. + * + * @type string $id ID of the integration (string all digits). + * @type string $external_business_id External business ID for the integration. + * @type string $connected_merchant_id Connected merchant ID for the integration. + * @type string $connected_user_id Connected user ID for the integration. + * @type string $connected_advertiser_id Connected advertiser ID for the integration. + * @type string $connected_lba_id Connected LBA ID for the integration. + * @type string $connected_tag_id Connected tag ID for the integration. + * @type int $partner_access_token_expiry Partner access token expiry for the integration. + * @type int $partner_refresh_token_expiry Partner refresh token expiry for the integration. + * @type string $scopes Scopes for the integration. + * @type int $created_timestamp Created timestamp for the integration. + * @type int $updated_timestamp Updated timestamp for the integration. + * @type string $additional_id_1 Additional ID 1 for the integration. + * @type string $partner_metadata Partner metadata for the integration. * } - * @throws PinterestApiException + * @throws PinterestApiException In case of 404, 409 and 500 errors from Pinterest. */ public static function update_commerce_integration( string $external_business_id, array $data ): array { return Pinterest\API\APIV5::make_request( "integrations/commerce/{$external_business_id}", - 'PATCH', - json_encode( $data ) + 'PATCH', + json_encode( $data ) ); } @@ -942,10 +944,10 @@ public static function update_commerce_integration( string $external_business_id * @since x.x.x * * @return bool - * @throws PinterestApiException + * @throws PinterestApiException In case of 500 unexpected error from Pinterest. */ public static function delete_commerce_integration(): bool { - $external_business_id = Pinterest_For_Woocommerce::get_data( 'integration_data' )['external_business_id']; + $external_business_id = self::get_data( 'integration_data' )['external_business_id']; Pinterest\API\APIV5::make_request( "integrations/commerce/{$external_business_id}", @@ -1214,7 +1216,8 @@ public static function get_linked_businesses( bool $force_refresh = false ): arr self::save_data( 'linked_businesses', $linked_businesses ); } - /*$linked_businesses = array_map( + /* + * $linked_businesses = array_map( function ( $business ) { return array( 'value' => $business->id, @@ -1222,7 +1225,8 @@ function ( $business ) { ); }, $linked_businesses - );*/ + ); + */ return $linked_businesses; } diff --git a/src/API/AdvertiserConnect.php b/src/API/AdvertiserConnect.php index 48fc7667a..6cac64a02 100644 --- a/src/API/AdvertiserConnect.php +++ b/src/API/AdvertiserConnect.php @@ -64,7 +64,7 @@ public function connect_advertiser( WP_REST_Request $request ) { // Automatic Enhanced Match is enabled by default (at least Pinterest said so). // if ( $enable_aem ) { // @TODO: We do not have an API v5 analog for this call. Commenting it out temporarily. - # self::enable_aem_tag( $tag_id ); + // self::enable_aem_tag( $tag_id ); // } $is_connected = Pinterest_For_Woocommerce()::get_data( 'is_advertiser_connected' ); From bc2fae1e6ae0c3a06c3dd800996cd9dae1402bac Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 1 Jun 2023 19:06:11 +0300 Subject: [PATCH 16/36] Fix code styling. --- src/API/AdvertiserConnect.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/API/AdvertiserConnect.php b/src/API/AdvertiserConnect.php index 6cac64a02..795453bcf 100644 --- a/src/API/AdvertiserConnect.php +++ b/src/API/AdvertiserConnect.php @@ -61,11 +61,13 @@ public function connect_advertiser( WP_REST_Request $request ) { throw new Exception( esc_html__( 'Missing advertiser or tag parameters.', 'pinterest-for-woocommerce' ), 400 ); } - // Automatic Enhanced Match is enabled by default (at least Pinterest said so). - // if ( $enable_aem ) { - // @TODO: We do not have an API v5 analog for this call. Commenting it out temporarily. - // self::enable_aem_tag( $tag_id ); - // } + /* + * @NOTE: Automatic Enhanced Match is enabled by default (at least Pinterest said so). + * if ( $enable_aem ) { + * @TODO: We do not have an API v5 analog for this call. Commenting it out temporarily. + * self::enable_aem_tag( $tag_id ); + * } + */ $is_connected = Pinterest_For_Woocommerce()::get_data( 'is_advertiser_connected' ); From a39f21cdbbb546ac7bcf5812c23d21f68870f8a2 Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 1 Jun 2023 19:50:40 +0300 Subject: [PATCH 17/36] Fixing bug with json encoding a request body. --- class-pinterest-for-woocommerce.php | 14 +++++------- src/API/APIV5.php | 22 +++++++++---------- tests/Unit/Api/APIV5Test.php | 2 +- tests/Unit/Api/AdvertiserConnectTest.php | 28 ++++++++++++------------ 4 files changed, 31 insertions(+), 35 deletions(-) diff --git a/class-pinterest-for-woocommerce.php b/class-pinterest-for-woocommerce.php index 5b50775ca..1bae8e6c5 100644 --- a/class-pinterest-for-woocommerce.php +++ b/class-pinterest-for-woocommerce.php @@ -860,13 +860,11 @@ public static function create_commerce_integration(): array { $response = Pinterest\API\APIV5::make_request( 'integrations/commerce', 'POST', - json_encode( - array( - 'external_business_id' => $external_business_id, - 'connected_merchant_id' => $connection_data['merchant_id'] ?? '', - 'connected_advertiser_id' => $connection_data['advertiser_id'] ?? '', - 'connected_tag_id' => $connection_data['tag_id'] ?? '', - ) + array( + 'external_business_id' => $external_business_id, + 'connected_merchant_id' => $connection_data['merchant_id'] ?? '', + 'connected_advertiser_id' => $connection_data['advertiser_id'] ?? '', + 'connected_tag_id' => $connection_data['tag_id'] ?? '', ) ); @@ -934,7 +932,7 @@ public static function update_commerce_integration( string $external_business_id return Pinterest\API\APIV5::make_request( "integrations/commerce/{$external_business_id}", 'PATCH', - json_encode( $data ) + $data ); } diff --git a/src/API/APIV5.php b/src/API/APIV5.php index 87b50e315..c1d217c05 100644 --- a/src/API/APIV5.php +++ b/src/API/APIV5.php @@ -39,7 +39,7 @@ public static function prepare_request( $endpoint, $method = 'POST', $payload = return array( 'url' => static::API_DOMAIN . "/{$endpoint}", 'method' => $method, - 'args' => $payload, + 'args' => wp_json_encode( $payload ), 'headers' => array( 'Pinterest-Woocommerce-Version' => PINTEREST_FOR_WOOCOMMERCE_VERSION, 'Content-Type' => 'application/json', @@ -207,17 +207,15 @@ public static function create_tag( $ad_account_id ) { return self::make_request( "ad_accounts/{$ad_account_id}/conversion_tags", 'POST', - json_encode( - array( - 'name' => $tag_name, - 'aem_enabled' => true, - 'md_frequency' => 1, - 'aem_fnln_enabled' => true, - 'aem_ph_enabled' => true, - 'aem_ge_enabled' => true, - 'aem_db_enabled' => true, - 'ae_loc_enabled' => true, - ) + array( + 'name' => $tag_name, + 'aem_enabled' => true, + 'md_frequency' => 1, + 'aem_fnln_enabled' => true, + 'aem_ph_enabled' => true, + 'aem_ge_enabled' => true, + 'aem_db_enabled' => true, + 'ae_loc_enabled' => true, ) ); } diff --git a/tests/Unit/Api/APIV5Test.php b/tests/Unit/Api/APIV5Test.php index 9a8992bb0..aabfbe6a5 100644 --- a/tests/Unit/Api/APIV5Test.php +++ b/tests/Unit/Api/APIV5Test.php @@ -36,7 +36,7 @@ function ( $response, $parsed_args, $url ) { 'code_snippet' => '', 'id' => '9876543210123456789', 'last_fired_time_ms' => 123456789, - 'name' => $parsed_args['body']['name'], + 'name' => 'Some tag name 42', 'status' => 'ACTIVE', 'version' => 'v1', 'configs' => array( diff --git a/tests/Unit/Api/AdvertiserConnectTest.php b/tests/Unit/Api/AdvertiserConnectTest.php index b6b85aad6..231ef2bbb 100644 --- a/tests/Unit/Api/AdvertiserConnectTest.php +++ b/tests/Unit/Api/AdvertiserConnectTest.php @@ -25,20 +25,20 @@ function ( $response, $parsed_args, $url ) { ), 'body' => json_encode( array( - "id" => "987654321234567890", - "external_business_id" => "cbi-1234567890", - "connected_merchant_id" => "cmi-1234567890", - "connected_user_id" => "cui-1234567890", - "connected_advertiser_id" => $parsed_args['body']['advertiser_id'], - "connected_lba_id" => "cli-1234567890", - "connected_tag_id" => $parsed_args['body']['tag_id'], - "partner_access_token_expiry" => 1621350033000, - "partner_refresh_token_expiry" => 1621350033000, - "scopes" => "s-c-o-p-e-s", - "created_timestamp" => 1621350033000, - "updated_timestamp" => 1621350033300, - "additional_id_1" => "ai1-1234567890", - "partner_metadata" => "partner-meta-data", + 'id' => '987654321234567890', + 'external_business_id' => 'cbi-1234567890', + 'connected_merchant_id' => 'cmi-1234567890', + 'connected_user_id' => 'cui-1234567890', + 'connected_advertiser_id' => 'ai-1234567890', + 'connected_lba_id' => 'cli-1234567890', + 'connected_tag_id' => 'ti-1234567890', + 'partner_access_token_expiry' => 1621350033000, + 'partner_refresh_token_expiry' => 1621350033000, + 'scopes' => 's-c-o-p-e-s', + 'created_timestamp' => 1621350033000, + 'updated_timestamp' => 1621350033300, + 'additional_id_1' => 'ai1-1234567890', + 'partner_metadata' => 'partner-meta-data', ) ), 'response' => array( From 5c9a6125cf58fd3f1e58cb838e3d974d11b1efb5 Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 1 Jun 2023 21:22:44 +0300 Subject: [PATCH 18/36] Redoing http response logging not to use http_response value which is missing when inside unit tests. --- src/Logger.php | 17 +++++++++-- tests/Unit/Api/APIV5Test.php | 16 +++------- tests/Unit/Api/AdvertiserConnectTest.php | 16 +++------- tests/Unit/Api/TagsTest.php | 37 ++++++++---------------- 4 files changed, 34 insertions(+), 52 deletions(-) diff --git a/src/Logger.php b/src/Logger.php index d5536e70c..8a60a0ec5 100644 --- a/src/Logger.php +++ b/src/Logger.php @@ -8,6 +8,8 @@ namespace Automattic\WooCommerce\Pinterest; +use WP_Error; + if ( ! defined( 'ABSPATH' ) ) { exit; } @@ -86,8 +88,8 @@ public static function log_request( $url, $args, $level = 'debug' ) { /** * Helper for Logging API responses. * - * @param array|\WP_Error $response The body of the response. - * @param string $level The default level/context of the message to be logged. + * @param array|WP_Error $response The body of the response. + * @param string $level The default level/context of the message to be logged. * * @return void */ @@ -96,7 +98,16 @@ public static function log_response( $response, $level = 'debug' ) { $level = 'error'; $data = $response->get_error_code() . ': ' . $response->get_error_message(); } else { - $data = $response['http_response']->get_response_object()->raw; + // Collecting response data. + $status = wp_remote_retrieve_response_code( $response ); + $message = wp_remote_retrieve_response_message( $response ); + $body = wp_remote_retrieve_body( $response ); + $headers = wp_remote_retrieve_headers( $response ); + if ( is_object( $headers ) ) { + $headers = $headers->getAll(); + } + + $data = 'Status: ' . $status . ' ' . $message . "\n\n" . 'Headers: ' . wp_json_encode( $headers ) . "\n\n" . 'Body: ' . $body; } self::log( 'Response: ' . "\n\n" . $data . "\n", $level ); diff --git a/tests/Unit/Api/APIV5Test.php b/tests/Unit/Api/APIV5Test.php index aabfbe6a5..c8aa1848b 100644 --- a/tests/Unit/Api/APIV5Test.php +++ b/tests/Unit/Api/APIV5Test.php @@ -10,9 +10,7 @@ use Automattic\WooCommerce\Pinterest\API\APIV5; use Automattic\WooCommerce\Pinterest\PinterestApiException; -use WP_HTTP_Requests_Response; use WP_UnitTestCase; -use WpOrg\Requests\Response; class APIV5Test extends WP_UnitTestCase { @@ -51,14 +49,11 @@ function ( $response, $parsed_args, $url ) { ) ), 'response' => array( - 'code' => 200, + 'code' => 200, + 'message' => 'OK', ), 'cookies' => array(), 'filename' => '', - 'http_response' => new WP_HTTP_Requests_Response( - new Response(), - '' - ), ); }, 10, @@ -106,14 +101,11 @@ function ( $response, $parsed_args, $url ) { ) ), 'response' => array( - 'code' => 0, + 'code' => 500, + 'message' => 'Unexpected error', ), 'cookies' => array(), 'filename' => '', - 'http_response' => new WP_HTTP_Requests_Response( - new Response(), - '' - ), ); }, 10, diff --git a/tests/Unit/Api/AdvertiserConnectTest.php b/tests/Unit/Api/AdvertiserConnectTest.php index 231ef2bbb..b55ddf6e9 100644 --- a/tests/Unit/Api/AdvertiserConnectTest.php +++ b/tests/Unit/Api/AdvertiserConnectTest.php @@ -10,8 +10,6 @@ use Automattic\WooCommerce\Pinterest\API\AdvertiserConnect; use Exception; -use WP_HTTP_Requests_Response; -use WpOrg\Requests\Response; class AdvertiserConnectTest extends \WP_UnitTestCase { @@ -42,14 +40,11 @@ function ( $response, $parsed_args, $url ) { ) ), 'response' => array( - 'code' => 200, + 'code' => 200, + 'message' => 'OK', ), 'cookies' => array(), 'filename' => '', - 'http_response' => new WP_HTTP_Requests_Response( - new Response(), - '' - ), ); }, 10, @@ -89,14 +84,11 @@ function ( $response, $parsed_args, $url ) { ) ), 'response' => array( - 'code' => 404, + 'code' => 404, + 'message' => 'Not Found', ), 'cookies' => array(), 'filename' => '', - 'http_response' => new WP_HTTP_Requests_Response( - new Response(), - '' - ), ); }, 10, diff --git a/tests/Unit/Api/TagsTest.php b/tests/Unit/Api/TagsTest.php index f64b27bd5..cb705eb25 100644 --- a/tests/Unit/Api/TagsTest.php +++ b/tests/Unit/Api/TagsTest.php @@ -8,10 +8,8 @@ namespace Automattic\WooCommerce\Pinterest\Tests\Unit\Api; -use WP_HTTP_Requests_Response; use WP_REST_Request; use WP_Test_REST_TestCase; -use WpOrg\Requests\Response; class TagsTest extends WP_Test_REST_TestCase { @@ -147,14 +145,11 @@ function ( $response, $parsed_args, $url ) use ( $enhanced_match) { ) ), 'response' => array( - 'code' => 200, + 'code' => 200, + 'message' => 'OK', ), 'cookies' => array(), 'filename' => '', - 'http_response' => new WP_HTTP_Requests_Response( - new Response(), - '' - ), ); }, 10, @@ -243,14 +238,11 @@ function ( $response, $parsed_args, $url ) { ), 'body' => json_encode( $body ), 'response' => array( - 'code' => 200, + 'code' => 200, + 'message' => 'OK', ), 'cookies' => array(), 'filename' => '', - 'http_response' => new WP_HTTP_Requests_Response( - new Response(), - '' - ), ); }, 10, @@ -304,10 +296,6 @@ function ( $response, $parsed_args, $url ) { ), 'cookies' => array(), 'filename' => '', - 'http_response' => new WP_HTTP_Requests_Response( - new Response(), - '' - ), ); }, 10, @@ -341,8 +329,9 @@ public function test_tags_endpoint_returns_tag_create_error() { add_filter( 'pre_http_request', function ( $response, $parsed_args, $url ) { - $body = array(); - $code = 200; + $body = array(); + $code = 200; + $message = 'OK'; // 1. GET tags list. if ( 'GET' === $parsed_args['method'] ) { @@ -353,8 +342,9 @@ function ( $response, $parsed_args, $url ) { // 2. POST new tag. if ( 'POST' === $parsed_args['method'] ) { - $code = 500; - $body = array( + $code = 500; + $message = 'Unexpected error'; + $body = array( 'code' => 0, 'message' => 'string', ); @@ -366,14 +356,11 @@ function ( $response, $parsed_args, $url ) { ), 'body' => json_encode( $body ), 'response' => array( - 'code' => $code, + 'code' => $code, + 'message' => $message, ), 'cookies' => array(), 'filename' => '', - 'http_response' => new WP_HTTP_Requests_Response( - new Response(), - '' - ), ); }, 10, From 7daa394cffe9faf744ab2531b0f99f7e80c6aac4 Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 1 Jun 2023 21:29:21 +0300 Subject: [PATCH 19/36] Adjust faked responses. --- tests/Unit/PinterestForWoocommerceTest.php | 30 ++++++---------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/tests/Unit/PinterestForWoocommerceTest.php b/tests/Unit/PinterestForWoocommerceTest.php index a66cbaaf4..2800b9250 100644 --- a/tests/Unit/PinterestForWoocommerceTest.php +++ b/tests/Unit/PinterestForWoocommerceTest.php @@ -5,9 +5,7 @@ use Automattic\WooCommerce\Pinterest\PinterestApiException; use Automattic\WooCommerce\Pinterest\RefreshToken; use Pinterest_For_Woocommerce; -use WP_HTTP_Requests_Response; use WP_UnitTestCase; -use WpOrg\Requests\Response; class PinterestForWoocommerceTest extends WP_UnitTestCase { @@ -50,14 +48,11 @@ function ( $key ) { ) ), 'response' => array( - 'code' => 200, + 'code' => 200, + 'message' => 'OK', ), 'cookies' => array(), 'filename' => '', - 'http_response' => new WP_HTTP_Requests_Response( - new Response(), - '' - ), ); }, 10, @@ -122,14 +117,11 @@ function ( $response, $parsed_args, $url ) { ) ), 'response' => array( - 'code' => 404, + 'code' => 404, + 'message' => 'Not Found', ), 'cookies' => array(), 'filename' => '', - 'http_response' => new WP_HTTP_Requests_Response( - new Response(), - '' - ), ); }, 10, @@ -173,14 +165,11 @@ function ( $response, $parsed_args, $url ) { ) ), 'response' => array( - 'code' => 409, + 'code' => 409, + 'message' => 'Conflict', ), 'cookies' => array(), 'filename' => '', - 'http_response' => new WP_HTTP_Requests_Response( - new Response(), - '' - ), ); }, 10, @@ -224,14 +213,11 @@ function ( $response, $parsed_args, $url ) { ) ), 'response' => array( - 'code' => 0, + 'code' => 500, + 'message' => 'Unexpected error', ), 'cookies' => array(), 'filename' => '', - 'http_response' => new WP_HTTP_Requests_Response( - new Response(), - '' - ), ); }, 10, From ed0457861bafdb4a74277940ec50ba5ce4394c67 Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 1 Jun 2023 22:08:46 +0300 Subject: [PATCH 20/36] Fix unit test response stubs. --- tests/Unit/Api/APIV5Test.php | 2 +- tests/Unit/PinterestForWoocommerceTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Unit/Api/APIV5Test.php b/tests/Unit/Api/APIV5Test.php index c8aa1848b..d14b241b2 100644 --- a/tests/Unit/Api/APIV5Test.php +++ b/tests/Unit/Api/APIV5Test.php @@ -84,7 +84,7 @@ function ( $response, $parsed_args, $url ) { public function test_create_tag_returns_unexpected_error() { $this->expectException( PinterestApiException::class ); - $this->expectExceptionCode( 0 ); + $this->expectExceptionCode( 500 ); $this->expectExceptionMessage( 'Any other message from Pinterest which falls under Unexpected error.' ); add_filter( diff --git a/tests/Unit/PinterestForWoocommerceTest.php b/tests/Unit/PinterestForWoocommerceTest.php index 2800b9250..02f0017ff 100644 --- a/tests/Unit/PinterestForWoocommerceTest.php +++ b/tests/Unit/PinterestForWoocommerceTest.php @@ -196,7 +196,7 @@ function ( $response, $parsed_args, $url ) { public function test_update_commerce_integration_returns_unexpected_error() { $this->expectException( PinterestApiException::class ); - $this->expectExceptionCode( 0 ); + $this->expectExceptionCode( 500 ); $this->expectExceptionMessage( 'Any other message from Pinterest which falls under Unexpected error.' ); add_filter( From b2f0555112ddf507a2599260f0a2400fbea084c6 Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 1 Jun 2023 22:26:39 +0300 Subject: [PATCH 21/36] Adjust JS unit tests to correspond to new Wizard UI. --- .../app/steps/ClaimWebsite.test.js | 5 +--- .../setup-guide/app/views/WizardApp.test.js | 24 ++----------------- 2 files changed, 3 insertions(+), 26 deletions(-) diff --git a/assets/source/setup-guide/app/steps/ClaimWebsite.test.js b/assets/source/setup-guide/app/steps/ClaimWebsite.test.js index b2c79c433..5c33b7bc7 100644 --- a/assets/source/setup-guide/app/steps/ClaimWebsite.test.js +++ b/assets/source/setup-guide/app/steps/ClaimWebsite.test.js @@ -20,7 +20,7 @@ jest.mock( '@wordpress/api-fetch', () => { */ import { recordEvent } from '@woocommerce/tracks'; import apiFetch from '@wordpress/api-fetch'; -import { fireEvent, render, waitFor } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; /** * Internal dependencies @@ -41,7 +41,6 @@ describe( 'Claim Website Record Events', () => { {} } view="wizard" /> ); - fireEvent.click( getByText( 'Start verification' ) ); expect( recordEvent ).toHaveBeenCalledWith( 'pfw_domain_verify_failure', expect.any( Object ) @@ -57,8 +56,6 @@ describe( 'Claim Website Record Events', () => { {} } view="wizard" /> ); - fireEvent.click( getByText( 'Start verification' ) ); - // Wait for async click handler and apiFetch resolution. await waitFor( () => expect( recordEvent ).toHaveBeenCalledWith( diff --git a/assets/source/setup-guide/app/views/WizardApp.test.js b/assets/source/setup-guide/app/views/WizardApp.test.js index 3176cd366..b45c03527 100644 --- a/assets/source/setup-guide/app/views/WizardApp.test.js +++ b/assets/source/setup-guide/app/views/WizardApp.test.js @@ -44,7 +44,6 @@ describe( 'WizardApp component', () => { test( 'should show all options and first step should be clickable', () => { expect( rendered.getByText( stepOne ) ).toBeInTheDocument(); expect( rendered.getByText( stepTwo ) ).toBeInTheDocument(); - expect( rendered.getByText( stepThree ) ).toBeInTheDocument(); expect( rendered.queryAllByRole( 'button' ).length ).toBe( 1 ); } ); @@ -83,7 +82,7 @@ describe( 'WizardApp component', () => { } ); test( 'should 3 steps button be clickable in the stepper', () => { - expect( rendered.queryAllByRole( 'button' ).length ).toBe( 3 ); + expect( rendered.queryAllByRole( 'button' ).length ).toBe( 2 ); const setUpButton = rendered.getByRole( 'button', { name: stepOne, @@ -127,7 +126,7 @@ describe( 'WizardApp component', () => { } ); test( 'should all three steps be clickable buttons', () => { - expect( rendered.queryAllByRole( 'button' ).length ).toBe( 3 ); + expect( rendered.queryAllByRole( 'button' ).length ).toBe( 2 ); expect( rendered.getByRole( 'button', { @@ -141,12 +140,6 @@ describe( 'WizardApp component', () => { exact: false, } ) ).toBeInTheDocument(); - expect( - rendered.getByRole( 'button', { - name: stepThree, - exact: false, - } ) - ).toBeInTheDocument(); } ); test( 'should event tracking be = claim-website after click', () => { @@ -168,18 +161,5 @@ describe( 'WizardApp component', () => { 'claim-website' ); } ); - - test( 'should event tracking be = setup-tracking after click', () => { - fireEvent.click( - rendered.getByRole( 'button', { - name: stepThree, - } ) - ); - - expect( recordEvent ).toHaveBeenCalledWith( 'pfw_setup', { - target: 'setup-tracking', - trigger: 'wizard-stepper', - } ); - } ); } ); } ); From 39b1da44170c0bf61f9bd9ad5fcdd3ddbbc7ce5c Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 1 Jun 2023 23:18:51 +0300 Subject: [PATCH 22/36] Fix JS lint errors. --- assets/source/catalog-sync/App.js | 14 ++--- .../OnboardingModals/OnboardingErrorModal.js | 4 +- .../components/OnboardingModals/index.js | 4 +- .../source/catalog-sync/sections/SyncState.js | 5 +- .../components/prelaunch-notice/index.js | 5 +- .../app/components/Account/Connection.js | 5 +- .../app/components/SyncSettings/index.js | 5 +- .../UnsupportedCountryNotice/index.js | 5 +- assets/source/setup-guide/app/constants.js | 5 +- .../setup-guide/app/steps/ClaimWebsite.js | 31 +++++----- .../app/steps/ClaimWebsite.test.js | 8 +-- .../setup-guide/app/steps/SetupAccount.js | 17 +++--- .../source/setup-guide/app/steps/SetupPins.js | 27 ++++----- .../setup-guide/app/steps/SetupTracking.js | 57 ++++++++----------- .../app/steps/components/AdsCreditsPromo.js | 6 +- .../setup-guide/app/views/LandingPageApp.js | 13 ++--- .../source/setup-guide/app/views/WizardApp.js | 2 - .../setup-guide/app/views/WizardApp.test.js | 1 - 18 files changed, 93 insertions(+), 121 deletions(-) diff --git a/assets/source/catalog-sync/App.js b/assets/source/catalog-sync/App.js index 26c5c20a5..1ce573175 100644 --- a/assets/source/catalog-sync/App.js +++ b/assets/source/catalog-sync/App.js @@ -46,16 +46,14 @@ import { useSettingsSelect } from '../setup-guide/app/helpers/effects'; const CatalogSyncApp = () => { const adsCampaignIsActive = useSettingsSelect()?.ads_campaign_is_active; - const couponRedeemErrorID = useSettingsSelect()?.account_data - ?.coupon_redeem_info?.error_id; + const couponRedeemErrorID = + useSettingsSelect()?.account_data?.coupon_redeem_info?.error_id; useCreateNotice( wcSettings.pinterest_for_woocommerce.error ); - const [ isOnboardingModalOpen, setIsOnboardingModalOpen ] = useState( - false - ); - const [ isAdCreditsNoticeOpen, setIsAdCreditsNoticeOpen ] = useState( - false - ); + const [ isOnboardingModalOpen, setIsOnboardingModalOpen ] = + useState( false ); + const [ isAdCreditsNoticeOpen, setIsAdCreditsNoticeOpen ] = + useState( false ); const userInteractions = useSelect( ( select ) => select( USER_INTERACTION_STORE_NAME ).getUserInteractions() diff --git a/assets/source/catalog-sync/components/OnboardingModals/OnboardingErrorModal.js b/assets/source/catalog-sync/components/OnboardingModals/OnboardingErrorModal.js index c631ccfe9..d304adffc 100644 --- a/assets/source/catalog-sync/components/OnboardingModals/OnboardingErrorModal.js +++ b/assets/source/catalog-sync/components/OnboardingModals/OnboardingErrorModal.js @@ -29,8 +29,8 @@ const OnboardingErrorModal = ( { onCloseModal } ) => { const NOT_AVAILABLE_IN_COUNTRY_OR_CURRENCY_ERROR = 2327; const WRONG_BILLING_PROFILE_ERROR = 2006; - const couponRedeemInfo = useSettingsSelect()?.account_data - ?.coupon_redeem_info; + const couponRedeemInfo = + useSettingsSelect()?.account_data?.coupon_redeem_info; let errorMessageText = ''; switch ( couponRedeemInfo?.error_id ) { diff --git a/assets/source/catalog-sync/components/OnboardingModals/index.js b/assets/source/catalog-sync/components/OnboardingModals/index.js index 0553ef2c5..72a6b7f20 100644 --- a/assets/source/catalog-sync/components/OnboardingModals/index.js +++ b/assets/source/catalog-sync/components/OnboardingModals/index.js @@ -16,8 +16,8 @@ import OnboardingErrorModal from './OnboardingErrorModal'; */ const OnboardingModals = ( { onCloseModal } ) => { const adsCampaignIsActive = useSettingsSelect()?.ads_campaign_is_active; - const couponRedeemInfo = useSettingsSelect()?.account_data - ?.coupon_redeem_info; + const couponRedeemInfo = + useSettingsSelect()?.account_data?.coupon_redeem_info; // Generic modal when there is no campaign. if ( ! adsCampaignIsActive ) { diff --git a/assets/source/catalog-sync/sections/SyncState.js b/assets/source/catalog-sync/sections/SyncState.js index c5093ed01..b08f4481e 100644 --- a/assets/source/catalog-sync/sections/SyncState.js +++ b/assets/source/catalog-sync/sections/SyncState.js @@ -41,8 +41,9 @@ const SyncState = () => { select( REPORTS_STORE_NAME ).getFeedState() ); - const hasAvailableCredits = useSettingsSelect()?.account_data - ?.available_discounts?.marketing_offer?.remaining_discount; + const hasAvailableCredits = + useSettingsSelect()?.account_data?.available_discounts?.marketing_offer + ?.remaining_discount; const availableCredits = sprintf( /* translators: %s credits value with currency formatted using wc_price */ diff --git a/assets/source/components/prelaunch-notice/index.js b/assets/source/components/prelaunch-notice/index.js index d93beb9f8..0321b4bee 100644 --- a/assets/source/components/prelaunch-notice/index.js +++ b/assets/source/components/prelaunch-notice/index.js @@ -37,9 +37,8 @@ const PrelaunchNotice = () => {

{ recordEvent( 'pfw_account_disconnect_button_click', { context } ); diff --git a/assets/source/setup-guide/app/components/SyncSettings/index.js b/assets/source/setup-guide/app/components/SyncSettings/index.js index 16f939e01..d71a3c75b 100644 --- a/assets/source/setup-guide/app/components/SyncSettings/index.js +++ b/assets/source/setup-guide/app/components/SyncSettings/index.js @@ -30,9 +30,8 @@ const SyncSettings = () => { const syncAppSettings = useSyncSettingsDispatch(); const createNotice = useCreateNotice(); const { removeNotice } = useDispatch( 'core/notices' ); - const [ triggeredSyncSettings, setTriggeredSyncSettings ] = useState( - false - ); + const [ triggeredSyncSettings, setTriggeredSyncSettings ] = + useState( false ); const syncSettings = async () => { try { diff --git a/assets/source/setup-guide/app/components/UnsupportedCountryNotice/index.js b/assets/source/setup-guide/app/components/UnsupportedCountryNotice/index.js index 10db4e080..5c741ebbe 100644 --- a/assets/source/setup-guide/app/components/UnsupportedCountryNotice/index.js +++ b/assets/source/setup-guide/app/components/UnsupportedCountryNotice/index.js @@ -63,9 +63,8 @@ function UnsupportedCountryNotice( { countryCode } ) { supportedCountriesLink: ( { if ( reqError?.data?.pinterest_code === undefined ) { @@ -94,12 +93,11 @@ const StaticError = ( { reqError } ) => { * @fires wcadmin_pfw_documentation_link_click with `{ link_id: 'claim-website', context: props.view }` * @param {Object} props React props. * @param {'wizard'|'settings'} props.view Indicate which view this component is rendered on. - * @param {Function} [props.goToNextStep] * When the website claim is complete, called when clicking the "Continue" button. * The "Continue" button is only displayed when `props.view` is 'wizard'. * @return {JSX.Element} Rendered component. */ -const ClaimWebsite = ( { goToNextStep, view } ) => { +const ClaimWebsite = ( { view } ) => { const [ status, setStatus ] = useState( STATUS.IDLE ); const [ reqError, setReqError ] = useState(); const isDomainVerified = useSettingsSelect( 'isDomainVerified' ); @@ -109,13 +107,16 @@ const ClaimWebsite = ( { goToNextStep, view } ) => { useEffect( () => { // If domain is not verified and verification status is not pending nor success - start verification. - if ( ! Object.values( LABEL_STATUS ).includes( status ) && ! isDomainVerified ) { + if ( + ! Object.values( LABEL_STATUS ).includes( status ) && + ! isDomainVerified + ) { handleClaimWebsite(); } if ( status !== STATUS.PENDING && isDomainVerified ) { setStatus( STATUS.SUCCESS ); } - }, [ status, isDomainVerified ] ); + }, [ status, isDomainVerified ] ); // eslint-disable-line const handleClaimWebsite = async () => { setStatus( STATUS.PENDING ); @@ -155,7 +156,10 @@ const ClaimWebsite = ( { goToNextStep, view } ) => { const VerifyButton = () => { const buttonLabels = { - [ STATUS.IDLE ]: __( 'Start verification', 'pinterest-for-woocommerce' ), + [ STATUS.IDLE ]: __( + 'Start verification', + 'pinterest-for-woocommerce' + ), [ STATUS.PENDING ]: __( 'Verifying…', 'pinterest-for-woocommerce' ), [ STATUS.ERROR ]: __( 'Try again', 'pinterest-for-woocommerce' ), [ STATUS.SUCCESS ]: __( 'Verified', 'pinterest-for-woocommerce' ), @@ -175,9 +179,13 @@ const ClaimWebsite = ( { goToNextStep, view } ) => { return ( @@ -515,8 +510,7 @@ const SetupTracking = ( { view = 'settings' } ) => { item.name, item.id ), - value: - item.id, + value: item.id, } ) ) } help={ __( @@ -554,14 +548,13 @@ const SetupTracking = ( { view = 'settings' } ) => { isLink { ...documentationLinkProps( { - href: - wcSettings - .pinterest_for_woocommerce - .countryTos - .terms_url, - linkId: - 'ad-terms-of-service', - context: view, + href: wcSettings + .pinterest_for_woocommerce + .countryTos + .terms_url, + linkId: 'ad-terms-of-service', + context: + view, } ) } > diff --git a/assets/source/setup-guide/app/steps/components/AdsCreditsPromo.js b/assets/source/setup-guide/app/steps/components/AdsCreditsPromo.js index 532976982..4037ce687 100644 --- a/assets/source/setup-guide/app/steps/components/AdsCreditsPromo.js +++ b/assets/source/setup-guide/app/steps/components/AdsCreditsPromo.js @@ -21,10 +21,8 @@ import GiftIcon from '../../components/GiftIcon'; const AdsCreditsPromo = () => { const appSettings = useSettingsSelect(); - const [ - isTermsAndConditionsModalOpen, - setIsTermsAndConditionsModalOpen, - ] = useState( false ); + const [ isTermsAndConditionsModalOpen, setIsTermsAndConditionsModalOpen ] = + useState( false ); const openTermsAndConditionsModal = () => { setIsTermsAndConditionsModalOpen( true ); diff --git a/assets/source/setup-guide/app/views/LandingPageApp.js b/assets/source/setup-guide/app/views/LandingPageApp.js index 848fe5cab..d3e88749c 100644 --- a/assets/source/setup-guide/app/views/LandingPageApp.js +++ b/assets/source/setup-guide/app/views/LandingPageApp.js @@ -138,10 +138,8 @@ const WelcomeSection = () => { * @return {JSX.Element} Rendered element. */ const AdsCreditSection = () => { - const [ - isTermsAndConditionsModalOpen, - setIsTermsAndConditionsModalOpen, - ] = useState( false ); + const [ isTermsAndConditionsModalOpen, setIsTermsAndConditionsModalOpen ] = + useState( false ); const openTermsAndConditionsModal = () => { setIsTermsAndConditionsModalOpen( true ); @@ -357,11 +355,8 @@ const FaqQuestion = ( { questionId, question, answer } ) => { }; const LandingPageApp = () => { - const { - pluginVersion, - isAdsSupportedCountry, - storeCountry, - } = wcSettings.pinterest_for_woocommerce; + const { pluginVersion, isAdsSupportedCountry, storeCountry } = + wcSettings.pinterest_for_woocommerce; const adsCampaignIsActive = useSettingsSelect()?.ads_campaign_is_active; diff --git a/assets/source/setup-guide/app/views/WizardApp.js b/assets/source/setup-guide/app/views/WizardApp.js index 1192d06ba..2c704066b 100644 --- a/assets/source/setup-guide/app/views/WizardApp.js +++ b/assets/source/setup-guide/app/views/WizardApp.js @@ -13,7 +13,6 @@ import { updateQueryString } from '@woocommerce/navigation'; */ import SetupAccount from '../steps/SetupAccount'; import ClaimWebsite from '../steps/ClaimWebsite'; -import SetupTracking from '../steps/SetupTracking'; import OnboardingTopBar from '../components/TopBar'; import TransientNotices from '../components/TransientNotices'; import { @@ -43,7 +42,6 @@ const WizardApp = ( { query } ) => { ); const appSettings = useSettingsSelect(); - const isDomainVerified = useSettingsSelect( 'isDomainVerified' ); const createNotice = useCreateNotice(); useEffect( () => { diff --git a/assets/source/setup-guide/app/views/WizardApp.test.js b/assets/source/setup-guide/app/views/WizardApp.test.js index b45c03527..d5e06d306 100644 --- a/assets/source/setup-guide/app/views/WizardApp.test.js +++ b/assets/source/setup-guide/app/views/WizardApp.test.js @@ -31,7 +31,6 @@ jest.mock( '../steps/SetupTracking', () => () => null ); const stepOne = /Set up your business account/; const stepTwo = /Claim your website/; -const stepThree = /Track conversions/; describe( 'WizardApp component', () => { describe( 'First rendering', () => { From 4b5dcd63577461f121d408d99b9a0ef08c6fa837 Mon Sep 17 00:00:00 2001 From: Dima Date: Fri, 2 Jun 2023 19:12:46 +0300 Subject: [PATCH 23/36] Remove redundant frontend save setting call after domain verification which was also corrupting the settings data. Add e2e test on auth successful hook. Add sync settings with Pinterest test. Update is domain verified method to check for the current domain verification status, instead of if current user has any domain verified with Pinterest. --- .../setup-guide/app/steps/ClaimWebsite.js | 5 +- class-pinterest-for-woocommerce.php | 22 +- i18n/languages/pinterest-for-woocommerce.pot | 257 +++++++++--------- src/API/APIV5.php | 45 ++- src/PinterestSyncSettings.php | 22 +- tests/E2e/PinterestConnectE2eTest.php | 229 ++++++++++++++++ tests/Unit/Api/SyncSettingsTest.php | 108 ++++++++ tests/Unit/PinterestForWoocommerceTest.php | 28 ++ 8 files changed, 559 insertions(+), 157 deletions(-) create mode 100644 tests/E2e/PinterestConnectE2eTest.php create mode 100644 tests/Unit/Api/SyncSettingsTest.php diff --git a/assets/source/setup-guide/app/steps/ClaimWebsite.js b/assets/source/setup-guide/app/steps/ClaimWebsite.js index 50563ffb2..12b85fb70 100644 --- a/assets/source/setup-guide/app/steps/ClaimWebsite.js +++ b/assets/source/setup-guide/app/steps/ClaimWebsite.js @@ -123,14 +123,11 @@ const ClaimWebsite = ( { view } ) => { setReqError(); try { - const results = await apiFetch( { + await apiFetch( { path: pfwSettings.apiRoute + '/domain_verification', method: 'POST', } ); - await setAppSettings( { account_data: results.account_data } ); - recordEvent( 'pfw_domain_verify_success' ); - setStatus( STATUS.SUCCESS ); } catch ( error ) { setStatus( STATUS.ERROR ); diff --git a/class-pinterest-for-woocommerce.php b/class-pinterest-for-woocommerce.php index 1bae8e6c5..a4616962d 100644 --- a/class-pinterest-for-woocommerce.php +++ b/class-pinterest-for-woocommerce.php @@ -828,6 +828,17 @@ public function maybe_inject_verification_code() { } } + public static function unverify_website() { + var_export( 'Unverify website.' ); + $website = wp_parse_url( get_home_url() )['host']; + $response = Pinterest\API\APIV5::make_request( + "user_account/websites?website={$website}", + 'DELETE' + ); + var_export( $response ); + exit; + } + /** * Connects WC to Pinterest. * @@ -1236,7 +1247,7 @@ function ( $business ) { */ public static function get_account_id() { $account_data = Pinterest_For_Woocommerce()::get_setting( 'account_data' ); - return isset( $account_data['id'] ) ? $account_data['id'] : false; + return $account_data['id'] ?? false; } /** @@ -1334,14 +1345,15 @@ public static function is_business_connected() { /** - * Checks whether we have verified our domain, by checking account_data as + * Checks whether we have verified our current domain, by checking account_data as * returned by Pinterest. * - * @return boolean + * @return bool */ - public static function is_domain_verified() { + public static function is_domain_verified(): bool { $account_data = self::get_setting( 'account_data' ); - return isset( $account_data['is_any_website_verified'] ) ? (bool) $account_data['is_any_website_verified'] : false; + $verified_domains = $account_data['verified_user_websites'] ?? array(); + return in_array( wp_parse_url( get_home_url() )['host'] ?? '' , $verified_domains ); } /** diff --git a/i18n/languages/pinterest-for-woocommerce.pot b/i18n/languages/pinterest-for-woocommerce.pot index a7c4688bc..710b3700d 100644 --- a/i18n/languages/pinterest-for-woocommerce.pot +++ b/i18n/languages/pinterest-for-woocommerce.pot @@ -2,14 +2,14 @@ # This file is distributed under the GPL-2.0+. msgid "" msgstr "" -"Project-Id-Version: Pinterest for WooCommerce 1.2.19\n" +"Project-Id-Version: Pinterest for WooCommerce 1.2.21\n" "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/pinterest-for-woocommerce\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2023-05-18T16:25:33+00:00\n" +"POT-Creation-Date: 2023-06-02T11:24:52+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.6.0\n" "X-Domain: pinterest-for-woocommerce\n" @@ -79,11 +79,11 @@ msgstr "" msgid "There was an error disconnecting the Advertiser. Please try again." msgstr "" -#: class-pinterest-for-woocommerce.php:982 +#: class-pinterest-for-woocommerce.php:1051 msgid "There was an error getting the account data." msgstr "" -#: class-pinterest-for-woocommerce.php:1220 +#: class-pinterest-for-woocommerce.php:1291 msgid "Pinterest for WooCommerce verification page" msgstr "" @@ -185,51 +185,43 @@ msgstr "" msgid "20 minutes" msgstr "" -#: src/API/AdvertiserConnect.php:58 +#: src/API/AdvertiserConnect.php:61 msgid "Missing advertiser or tag parameters." msgstr "" #. Translators: The error description as returned from the API -#: src/API/AdvertiserConnect.php:81 +#: src/API/AdvertiserConnect.php:88 msgid "Could not connect advertiser with Pinterest. [%s]" msgstr "" -#: src/API/AdvertiserConnect.php:101 -msgid "The advertiser could not be connected to Pinterest." -msgstr "" - -#: src/API/AdvertiserConnect.php:105 -msgid "Incorrect advertiser ID." -msgstr "" - -#: src/API/AdvertiserConnect.php:154 -#: src/API/AdvertiserConnect.php:168 +#: src/API/AdvertiserConnect.php:171 +#: src/API/AdvertiserConnect.php:185 msgid "The advertiser could not be disconnected from Pinterest." msgstr "" #. Translators: The error description as returned from the API -#: src/API/Advertisers.php:80 +#: src/API/Advertisers.php:71 msgid "Could not fetch advertisers for Pinterest account ID. [%s]" msgstr "" -#: src/API/Auth.php:71 +#: src/API/Auth.php:72 msgid "Something went wrong with your attempt to authorize this App. Please try again." msgstr "" -#: src/API/Auth.php:98 +#: src/API/Auth.php:99 msgid "Token data missing, please try again later." msgstr "" -#: src/API/Auth.php:103 +#: src/API/Auth.php:104 msgid "Connection information missing, please try again later." msgstr "" -#: src/API/Auth.php:121 +#: src/API/Auth.php:122 msgid "There was an error getting the account data. Please try again later." msgstr "" #. Translators: 1: Request method 2: Request endpoint 3: Response status code 4: Response message 5: Pinterest code -#: src/API/Base.php:114 +#: src/API/Base.php:115 msgid "" "%1$s Request: %2$s\n" "Status Code: %3$s\n" @@ -238,23 +230,23 @@ msgid "" msgstr "" #. Translators: 1: Request method 2: Request endpoint 3: Response status code 4: Response message -#: src/API/Base.php:136 +#: src/API/Base.php:137 msgid "" "%1$s Request: %2$s\n" "Status Code: %3$s\n" "API response: %4$s" msgstr "" -#: src/API/Base.php:300 +#: src/API/Base.php:301 msgid "Reconnect to your Pinterest account" msgstr "" -#: src/API/Base.php:355 +#: src/API/Base.php:356 msgid "Empty body" msgstr "" -#: src/API/Base.php:426 -#: src/API/Base.php:527 +#: src/API/Base.php:427 +#: src/API/Base.php:838 #: src/Merchants.php:139 msgid "Auto-created by Pinterest for WooCommerce" msgstr "" @@ -453,29 +445,38 @@ msgstr "" msgid "Pinterest returned: %1$s" msgstr "" -#: src/API/HealthCheck.php:65 +#: src/API/Health.php:69 msgid "Could not get approval status from Pinterest." msgstr "" #. Translators: The error description as returned from the API -#: src/API/HealthCheck.php:79 +#: src/API/Health.php:83 msgid "Could not fetch account status. [%s]" msgstr "" -#: src/API/Options.php:67 +#: src/API/Settings.php:68 msgid "Missing option parameters." msgstr "" -#: src/API/Options.php:73 +#: src/API/Settings.php:74 msgid "There was an error saving the settings." msgstr "" -#: src/API/Tags.php:51 +#: src/API/Tags.php:50 msgid "Advertiser missing" msgstr "" +#: src/API/Tags.php:56 +#: src/PinterestSyncSettings.php:113 +msgid "Response error" +msgstr "" + +#: src/API/Tags.php:65 +msgid "Could not create a tag. Please check the logs for additional information." +msgstr "" + #. Translators: The error description as returned from the API -#: src/API/Tags.php:85 +#: src/API/Tags.php:80 msgid "No tracking tag available. [%s]" msgstr "" @@ -637,10 +638,6 @@ msgstr "" msgid "Tracking advertiser or tag missing" msgstr "" -#: src/PinterestSyncSettings.php:113 -msgid "Response error" -msgstr "" - #. translators: plugin version. #: src/PluginUpdate.php:157 msgid "Plugin updated to version: %s." @@ -812,15 +809,15 @@ msgid "Issues" msgstr "" #. translators: %s credits value with currency formatted using wc_price -#: assets/source/catalog-sync/sections/SyncState.js:49 +#: assets/source/catalog-sync/sections/SyncState.js:50 msgid "You have %s of free ad credits left to use" msgstr "" -#: assets/source/catalog-sync/sections/SyncState.js:60 +#: assets/source/catalog-sync/sections/SyncState.js:61 msgid "Overview" msgstr "" -#: assets/source/catalog-sync/sections/SyncState.js:74 +#: assets/source/catalog-sync/sections/SyncState.js:75 msgid "Create ads to increase your reach with the Pinterest ads manager" msgstr "" @@ -860,7 +857,7 @@ msgstr "" msgid "The integration is only available to approved stores participating in the beta program." msgstr "" -#: assets/source/components/prelaunch-notice/index.js:49 +#: assets/source/components/prelaunch-notice/index.js:48 msgid "Click here for more information." msgstr "" @@ -869,7 +866,7 @@ msgid "Select a business account" msgstr "" #: assets/source/setup-guide/app/components/Account/BusinessAccountSelection.js:90 -#: assets/source/setup-guide/app/components/Account/Connection.js:245 +#: assets/source/setup-guide/app/components/Account/Connection.js:244 msgid "Connect" msgstr "" @@ -885,39 +882,39 @@ msgstr "" msgid "Create business account" msgstr "" -#: assets/source/setup-guide/app/components/Account/Connection.js:115 +#: assets/source/setup-guide/app/components/Account/Connection.js:114 msgid "Are you sure?" msgstr "" -#: assets/source/setup-guide/app/components/Account/Connection.js:122 +#: assets/source/setup-guide/app/components/Account/Connection.js:121 msgid "Are you sure you want to disconnect this account?" msgstr "" -#: assets/source/setup-guide/app/components/Account/Connection.js:133 +#: assets/source/setup-guide/app/components/Account/Connection.js:132 msgid "Yes, I'm sure" msgstr "" -#: assets/source/setup-guide/app/components/Account/Connection.js:139 +#: assets/source/setup-guide/app/components/Account/Connection.js:138 msgid "Cancel" msgstr "" -#: assets/source/setup-guide/app/components/Account/Connection.js:168 +#: assets/source/setup-guide/app/components/Account/Connection.js:167 msgid "There was a problem while trying to disconnect." msgstr "" -#: assets/source/setup-guide/app/components/Account/Connection.js:192 +#: assets/source/setup-guide/app/components/Account/Connection.js:191 msgid "Business account" msgstr "" -#: assets/source/setup-guide/app/components/Account/Connection.js:196 +#: assets/source/setup-guide/app/components/Account/Connection.js:195 msgid "Personal account" msgstr "" -#: assets/source/setup-guide/app/components/Account/Connection.js:214 +#: assets/source/setup-guide/app/components/Account/Connection.js:213 msgid "Disconnect" msgstr "" -#: assets/source/setup-guide/app/components/Account/Connection.js:225 +#: assets/source/setup-guide/app/components/Account/Connection.js:224 msgid "Connect your Pinterest Account" msgstr "" @@ -971,7 +968,6 @@ msgid "Could not fetch account status." msgstr "" #: assets/source/setup-guide/app/components/SaveSettingsButton.js:75 -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:202 #: assets/source/setup-guide/app/steps/SetupTracking.js:301 msgid "The advertiser was connected successfully." msgstr "" @@ -996,23 +992,23 @@ msgstr "" msgid "Save changes" msgstr "" -#: assets/source/setup-guide/app/components/SyncSettings/index.js:43 +#: assets/source/setup-guide/app/components/SyncSettings/index.js:42 msgid "Settings successfully synced with Pinterest Ads Manager." msgstr "" -#: assets/source/setup-guide/app/components/SyncSettings/index.js:54 +#: assets/source/setup-guide/app/components/SyncSettings/index.js:53 msgid "Failed to sync settings with Pinterest Ads Manager." msgstr "" -#: assets/source/setup-guide/app/components/SyncSettings/index.js:92 +#: assets/source/setup-guide/app/components/SyncSettings/index.js:91 msgid "Syncing settings" msgstr "" -#: assets/source/setup-guide/app/components/SyncSettings/index.js:100 +#: assets/source/setup-guide/app/components/SyncSettings/index.js:99 msgid "Sync to get latest settings from Pinterest Ads Manager" msgstr "" -#: assets/source/setup-guide/app/components/SyncSettings/index.js:107 +#: assets/source/setup-guide/app/components/SyncSettings/index.js:106 msgid "Sync" msgstr "" @@ -1164,7 +1160,7 @@ msgid "Merchant's website displays several pop-up messages" msgstr "" #: assets/source/setup-guide/app/constants.js:155 -#: assets/source/setup-guide/app/constants.js:233 +#: assets/source/setup-guide/app/constants.js:232 msgid "Merchant does not meet minimum website quality requirements" msgstr "" @@ -1184,43 +1180,43 @@ msgstr "" msgid "We recently updated our merchant guidelines and have found that your account is currently not in compliance with our guidelines. Merchants who do not comply with our guidelines will not be able to distribute or promote product Pins from their catalog on Pinterest. If you’d like to appeal this decision, review our guidelines for more detailed information on how you can get your products on Pinterest." msgstr "" -#: assets/source/setup-guide/app/constants.js:193 +#: assets/source/setup-guide/app/constants.js:192 msgid "Resale marketplaces are not allowed" msgstr "" -#: assets/source/setup-guide/app/constants.js:197 +#: assets/source/setup-guide/app/constants.js:196 msgid "Affiliate links are not allowed" msgstr "" -#: assets/source/setup-guide/app/constants.js:201 +#: assets/source/setup-guide/app/constants.js:200 msgid "Account does not meet the website requirements for verification" msgstr "" -#: assets/source/setup-guide/app/constants.js:205 +#: assets/source/setup-guide/app/constants.js:204 msgid "Account does not meet the product requirements for verification" msgstr "" -#: assets/source/setup-guide/app/constants.js:209 +#: assets/source/setup-guide/app/constants.js:208 msgid "Account does not meet the brand reputation criteria for verification" msgstr "" -#: assets/source/setup-guide/app/constants.js:213 +#: assets/source/setup-guide/app/constants.js:212 msgid "The template of the website is incomplete" msgstr "" -#: assets/source/setup-guide/app/constants.js:217 +#: assets/source/setup-guide/app/constants.js:216 msgid "Merchant does not meet community guidelines" msgstr "" -#: assets/source/setup-guide/app/constants.js:221 +#: assets/source/setup-guide/app/constants.js:220 msgid "Merchant has exceeded number of reported ads" msgstr "" -#: assets/source/setup-guide/app/constants.js:225 +#: assets/source/setup-guide/app/constants.js:224 msgid "Merchant has exceeded number of user reports" msgstr "" -#: assets/source/setup-guide/app/constants.js:229 +#: assets/source/setup-guide/app/constants.js:228 msgid "Merchant does not meet minimum product requirements" msgstr "" @@ -1245,74 +1241,72 @@ msgid "Erase Plugin Data" msgstr "" #. translators: %s: error reason returned by Pinterest when verifying website claim fail. -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:54 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:53 msgid "We were unable to verify this domain. %s" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:141 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:139 msgid "Couldn’t verify your domain." msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:158 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:156 msgid "Start verification" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:159 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:160 msgid "Verifying…" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:160 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:161 msgid "Try again" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:161 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:162 msgid "Verified" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:181 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:172 #: assets/source/setup-guide/app/steps/SetupTracking.js:273 msgid "Try Again" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:182 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:173 #: assets/source/setup-guide/app/steps/SetupTracking.js:274 msgid "Complete Setup" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:220 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:197 +msgid "Connected successfully." +msgstr "" + +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:212 #: assets/source/setup-guide/app/steps/SetupTracking.js:320 msgid "There was a problem connecting the advertiser." msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:232 -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:245 -#: assets/source/setup-guide/app/views/WizardApp.js:77 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:224 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:237 +#: assets/source/setup-guide/app/views/WizardApp.js:75 msgid "Claim your website" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:236 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:228 msgid "Step Two" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:249 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:241 msgid "Verified domain" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:270 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:262 msgid "Verify your domain to claim your website" msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:276 +#: assets/source/setup-guide/app/steps/ClaimWebsite.js:268 msgid "This will allow access to analytics for the Pins you publish from your site, the analytics on Pins that other people create from your site, and let people know where they can find more of your content." msgstr "" -#: assets/source/setup-guide/app/steps/ClaimWebsite.js:306 -#: assets/source/setup-guide/app/steps/SetupAccount.js:247 -#: assets/source/setup-guide/app/steps/SetupTracking.js:272 -msgid "Continue" -msgstr "" - -#: assets/source/setup-guide/app/steps/components/AdsCreditsPromo.js:57 +#: assets/source/setup-guide/app/steps/components/AdsCreditsPromo.js:55 msgid "As a new Pinterest customer, you can get $125 in free ad credits when you successfully set up Pinterest for WooCommerce and spend $15 on Pinterest Ads. Pinterest Terms and conditions apply." msgstr "" @@ -1321,7 +1315,7 @@ msgid "Couldn’t retrieve your linked business accounts." msgstr "" #: assets/source/setup-guide/app/steps/SetupAccount.js:109 -#: assets/source/setup-guide/app/views/WizardApp.js:62 +#: assets/source/setup-guide/app/views/WizardApp.js:60 msgid "Set up your business account" msgstr "" @@ -1341,14 +1335,19 @@ msgstr "" msgid "Set up a free Pinterest business account to get access to analytics on your Pins and the ability to run ads. This requires agreeing to our advertising guidelines and following our merchant guidelines." msgstr "" -#: assets/source/setup-guide/app/steps/SetupAccount.js:208 +#: assets/source/setup-guide/app/steps/SetupAccount.js:205 msgid "Or, create a new Pinterest account" msgstr "" -#: assets/source/setup-guide/app/steps/SetupAccount.js:235 +#: assets/source/setup-guide/app/steps/SetupAccount.js:232 msgid "Or, convert your personal account" msgstr "" +#: assets/source/setup-guide/app/steps/SetupAccount.js:244 +#: assets/source/setup-guide/app/steps/SetupTracking.js:272 +msgid "Continue" +msgstr "" + #: assets/source/setup-guide/app/steps/SetupPins.js:73 msgid "Publish Pins and Rich Pins" msgstr "" @@ -1377,44 +1376,44 @@ msgstr "" msgid "Matches conversion data with the person responsible for the conversion and lets you track cross-device checkouts. Requires Track Conversion option to be enabled. See more" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:176 +#: assets/source/setup-guide/app/steps/SetupPins.js:175 msgid "Automatic Enhanced Match support" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:182 +#: assets/source/setup-guide/app/steps/SetupPins.js:181 msgid "Uses hashed information that your customers have already provided to your business to help match more of your website visitors and conversions to people on Pinterest. Enabling it may improve the performance of your campaigns and can help increase the size of your Pinterest tag audiences." msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:206 +#: assets/source/setup-guide/app/steps/SetupPins.js:205 msgid "Manage information shared on Pinterest Ads Manager " msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:235 +#: assets/source/setup-guide/app/steps/SetupPins.js:232 msgid "Rich Pins" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:241 +#: assets/source/setup-guide/app/steps/SetupPins.js:238 msgid "Add Rich Pins for Products" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:247 +#: assets/source/setup-guide/app/steps/SetupPins.js:244 msgid "Automatically create and update rich pins on Pinterest for all synced products." msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:264 +#: assets/source/setup-guide/app/steps/SetupPins.js:261 msgid "Add Rich Pins for Posts" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:270 +#: assets/source/setup-guide/app/steps/SetupPins.js:267 msgid "Automatically create and update rich pins on Pinterest for posts." msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:290 -#: assets/source/setup-guide/app/steps/SetupPins.js:296 +#: assets/source/setup-guide/app/steps/SetupPins.js:287 +#: assets/source/setup-guide/app/steps/SetupPins.js:293 msgid "Save to Pinterest" msgstr "" -#: assets/source/setup-guide/app/steps/SetupPins.js:302 +#: assets/source/setup-guide/app/steps/SetupPins.js:299 msgid "Adds a ‘Save’ button on images allowing customers to save things straight from your website to Pinterest." msgstr "" @@ -1455,39 +1454,39 @@ msgstr "" msgid "The Pinterest tag is a piece of JavaScript code you put on your website to gather conversion insights and build audiences to target based on actions people have taken on your site." msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:381 +#: assets/source/setup-guide/app/steps/SetupTracking.js:380 msgid "Using conversion tags means you agree to our Ad Guidelines and Ad Data Terms." msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:419 +#: assets/source/setup-guide/app/steps/SetupTracking.js:416 msgid "Automatic Enhanced Match is enabled by default to match more of your website visitors and conversions to people on Pinterest. You can manage this in Settings." msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:456 +#: assets/source/setup-guide/app/steps/SetupTracking.js:451 msgid "Advertiser" msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:480 +#: assets/source/setup-guide/app/steps/SetupTracking.js:475 msgid "Select the advertiser for which you would like to install a tracking snippet." msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:493 +#: assets/source/setup-guide/app/steps/SetupTracking.js:488 msgid "Tracking Tag" msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:522 +#: assets/source/setup-guide/app/steps/SetupTracking.js:516 msgid "Select the tracking tag to use." msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:539 +#: assets/source/setup-guide/app/steps/SetupTracking.js:533 msgid "In order to proceed you need to read and accept the contents of the Pinterest Advertising Agreement." msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:547 +#: assets/source/setup-guide/app/steps/SetupTracking.js:541 msgid "I accept the Pinterest Advertising Agreement" msgstr "" -#: assets/source/setup-guide/app/steps/SetupTracking.js:594 +#: assets/source/setup-guide/app/steps/SetupTracking.js:587 msgid "An error occurred while attempting to fetch Advertisers & Tags from Pinterest. Please try again." msgstr "" @@ -1507,63 +1506,63 @@ msgstr "" msgid "By clicking ‘Get started’, you agree to our Terms of Service." msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:176 +#: assets/source/setup-guide/app/views/LandingPageApp.js:174 msgid "Try Pinterest for WooCommerce and get $125 in ad credits!" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:183 +#: assets/source/setup-guide/app/views/LandingPageApp.js:181 msgid "To help you get started with Pinterest Ads, new Pinterest customers can get $125 in ad credits when they have successfully set up Pinterest for WooCommerce and spend $15 on Pinterest Ads. Pinterest Terms and conditions apply." msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:219 +#: assets/source/setup-guide/app/views/LandingPageApp.js:217 msgid "Sync your catalog" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:223 +#: assets/source/setup-guide/app/views/LandingPageApp.js:221 msgid "Connect your store to seamlessly sync your product catalog with Pinterest and create rich pins for each item. Your pins are kept up to date with daily automatic updates." msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:233 +#: assets/source/setup-guide/app/views/LandingPageApp.js:231 msgid "Increase organic reach" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:237 +#: assets/source/setup-guide/app/views/LandingPageApp.js:235 msgid "Pinterest users can easily discover, save and buy products from your website without any advertising spend from you. Track your performance with the Pinterest tag." msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:247 +#: assets/source/setup-guide/app/views/LandingPageApp.js:245 msgid "Create a storefront on Pinterest" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:251 +#: assets/source/setup-guide/app/views/LandingPageApp.js:249 msgid "Syncing your catalog creates a Shop tab on your Pinterest profile which allows Pinterest users to easily discover your products." msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:275 +#: assets/source/setup-guide/app/views/LandingPageApp.js:273 msgid "Frequently asked questions" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:282 +#: assets/source/setup-guide/app/views/LandingPageApp.js:280 msgid "Why am I getting an “Account not connected” error message?" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:286 +#: assets/source/setup-guide/app/views/LandingPageApp.js:284 msgid "Your password might have changed recently. Click Reconnect Pinterest Account and follow the instructions on screen to restore the connection." msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:293 +#: assets/source/setup-guide/app/views/LandingPageApp.js:291 msgid "I have more than one Pinterest Advertiser account. Can I connect my WooCommerce store to multiple Pinterest Advertiser accounts?" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:297 +#: assets/source/setup-guide/app/views/LandingPageApp.js:295 msgid "Only one Pinterest advertiser account can be linked to each WooCommerce store. If you want to connect a different Pinterest advertiser account you will need to either Disconnect the existing Pinterest Advertiser account from your current WooCommerce store and connect a different Pinterest Advertiser account, or Create another WooCommerce store and connect the additional Pinterest Advertiser account." msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:304 +#: assets/source/setup-guide/app/views/LandingPageApp.js:302 msgid "How do I redeem the $125 ad credit from Pinterest?" msgstr "" -#: assets/source/setup-guide/app/views/LandingPageApp.js:308 +#: assets/source/setup-guide/app/views/LandingPageApp.js:306 msgid "To be eligible and redeem the $125 ad credit from Pinterest, you must complete the setup of Pinterest for WooCommerce, set up your billing with Pinterest Ads manager, and spend $15 with Pinterest ads. Ad credits may vary by country and is subject to availability. Credits may take up to 24 hours to be credited to the user. Each user is only eligible to receive the ad credits once." msgstr "" diff --git a/src/API/APIV5.php b/src/API/APIV5.php index c1d217c05..21654be25 100644 --- a/src/API/APIV5.php +++ b/src/API/APIV5.php @@ -39,7 +39,7 @@ public static function prepare_request( $endpoint, $method = 'POST', $payload = return array( 'url' => static::API_DOMAIN . "/{$endpoint}", 'method' => $method, - 'args' => wp_json_encode( $payload ), + 'args' => ! empty( $payload ) ? wp_json_encode( $payload ) : array(), 'headers' => array( 'Pinterest-Woocommerce-Version' => PINTEREST_FOR_WOOCOMMERCE_VERSION, 'Content-Type' => 'application/json', @@ -168,6 +168,47 @@ public static function get_advertiser_tags( $ad_account_id ) { return self::make_request( "ad_accounts/{$ad_account_id}/conversion_tags", 'GET' ); } + /** + * Get the advertiser's tracking tag config and details. + * + * @link https://developers.pinterest.com/docs/api/v5/#operation/conversion_tags/get + * + * @since x.x.x + * + * @param string $ad_account_id Ad account ID. + * @param string $conversion_tag_id Conversion tag ID. + * + * @return mixed|array { + * Tag object. + * + * @type string $ad_account_id Ad account ID. + * @type string $code_snippet Tag code snippet. + * @type string $enhanced_match_status Enum: "UNKNOWN" "NOT_VALIDATED" "VALIDATING_IN_PROGRESS" "VALIDATION_COMPLETE" null + * The enhanced match status of the tag + * @type string $id Tag ID. + * @type int $last_fired_time_ms Time for the last event fired. + * @type string $name Conversion tag name. + * @type string $status Enum: "ACTIVE" "PAUSED" "ARCHIVED" + * Entity status + * @type string $version Version number. + * @type array $configs { + * Tag Enhanced Match configuration. + * + * @type bool $aem_enabled Whether Automatic Enhanced Match email is enabled. + * @type int $md_frequency Metadata ingestion frequency. + * @type bool $aem_fnln_enabled Whether Automatic Enhanced Match name is enabled. + * @type bool $aem_ph_enabled Whether Automatic Enhanced Match phone is enabled. + * @type bool $aem_ge_enabled Whether Automatic Enhanced Match gender is enabled. + * @type bool $aem_db_enabled Whether Automatic Enhanced Match birthdate is enabled. + * @type bool $aem_loc_enabled Whether Automatic Enhanced Match location is enabled. + * } + * } + * @throws ApiException Throws 500 exception in case of unexpected error. + */ + public static function get_advertiser_tag( $ad_account_id, $conversion_tag_id ) { + return self::make_request( "ad_accounts/{$ad_account_id}/conversion_tags/{$conversion_tag_id}", 'GET' ); + } + /** * Create a tag for the given advertiser. * @@ -237,7 +278,7 @@ public static function create_tag( $ad_account_id ) { * @throws PinterestApiException If the request fails with 403 or 500 status. */ public static function domain_verification_data(): array { - return self::make_request( 'user_account/websites/verification' ); + return self::make_request( 'user_account/websites/verification', 'GET' ); } /** diff --git a/src/PinterestSyncSettings.php b/src/PinterestSyncSettings.php index a3c38925a..6553d93c2 100644 --- a/src/PinterestSyncSettings.php +++ b/src/PinterestSyncSettings.php @@ -8,9 +8,8 @@ namespace Automattic\WooCommerce\Pinterest; -use \Exception; -use Automattic\WooCommerce\Pinterest\API\Base; -use Automattic\WooCommerce\Pinterest\Logger; +use Automattic\WooCommerce\Pinterest\API\APIV5; +use Exception; use DateTime; if ( ! defined( 'ABSPATH' ) ) { @@ -56,7 +55,6 @@ public static function sync_settings() { } $formatted_synced_time = new DateTime(); - $formatted_synced_time = $formatted_synced_time->format( 'j M Y, h:i:s a' ); Pinterest_For_Woocommerce()::save_setting( 'last_synced_settings', $formatted_synced_time ); @@ -97,9 +95,7 @@ private static function sync_setting( $setting ) { * @throws Exception PHP Exception. */ private static function automatic_enhanced_match_support() { - try { - $advertiser_id = Pinterest_For_WooCommerce()::get_setting( 'tracking_advertiser' ); $tag_id = Pinterest_For_WooCommerce()::get_setting( 'tracking_tag' ); @@ -107,22 +103,14 @@ private static function automatic_enhanced_match_support() { throw new Exception( esc_html__( 'Tracking advertiser or tag missing', 'pinterest-for-woocommerce' ), 400 ); } - $response = Base::get_advertiser_tag( $advertiser_id, $tag_id ); - - if ( 'success' !== $response['status'] ) { - throw new Exception( esc_html__( 'Response error', 'pinterest-for-woocommerce' ), 400 ); - } - - $automatic_enhanced_match_support = $response['data']->configs->aem_enabled; - + $response = APIV5::get_advertiser_tag( $advertiser_id, $tag_id ); + $automatic_enhanced_match_support = $response['configs']['aem_enabled'] ?? false; Pinterest_For_Woocommerce()::save_setting( 'automatic_enhanced_match_support', $automatic_enhanced_match_support ); - } catch ( Exception $th ) { - Logger::log( $th->getMessage(), 'error' ); + throw new Exception( esc_html__( 'Response error', 'pinterest-for-woocommerce' ), 400 ); } return Pinterest_For_Woocommerce()::get_setting( 'automatic_enhanced_match_support' ); } - } diff --git a/tests/E2e/PinterestConnectE2eTest.php b/tests/E2e/PinterestConnectE2eTest.php new file mode 100644 index 000000000..fbaa59832 --- /dev/null +++ b/tests/E2e/PinterestConnectE2eTest.php @@ -0,0 +1,229 @@ + 'def50200fd71a25bca8b0732a0449818bb58774bc022a75b6a954d233aa8fc06f31fbed01e816cc6d2518a24ecd1018878d1568341b5412a52b33ef4b3627b296d76a6fdd53a9541dfe57bc8a304a837779b78c42b44516c473948941c928d68c9277db225931e3bfd87bfd36212d49018650d167c4fffc1600b27d76bd5debb6733aef64ff4c1de40740f10417f9c98145a12d994bad15b750cf0dbb48ac2ce78e8cec610707e3a087df58c90445ed4fb5acafbb6c60a07f4', + 'refresh_token' => 'def50200c637373b990d843d5be0b37415610a077359085993d42b7ef032ae368ab2a28e02e90438b5a34fcd92f62d938f65a17de11574743126cddefb30a662440881fe9197911940fddea93b3e0d32e34e4bd348bb74586d4980d304dc6a4b9cc577bc4533f32df239fb91044f4c17dd72842d2900a6aea8518939ddebc4073d11f2c61b92430272451c71a878e70b651dce2214657bd0b333c9517b2694168eb8431fa6c7d82d15720ffea853ea052382cbbcf2f4d2826851dcae7e1c3295433bd9cd586a21a6642ebbb6d5158f8c2a5f8c4f62c8b524ffbb5f13a90df9078feaf1ddda4627b1398eab80a3b57efac3de38cbafd162496f7908dd0cdc48752c4ef26848788fec7e7e48adacadf3144894ea6a1166dacda3649885709aacf049de326bef0f366dd776908b95024a1ef6c0b320ef091a104a127e1b2a5c7600e5b07af918ded1705989431e1ad84fac85c4da4ffc5f29cb3c6753b51eebf3b577bf73422c6d5750fcbb25b83e593c9e5ae03ddbbc5a59214b9e4ec89a93cd7e621a44889dd5e68e03e755b40bb9b1402ad3d327b16ebcc3f92e487929fdb22d9ed5a3a28400f04d17f492592488651c71edb5b93a7c87ee0043ad21a96989dd66cde55dd6ebbd0ba6e23040e809674cdb71bdb68d95183925e2702ab3188815bdddd49a4d76403f3190a8b2011a1e6440e2032589ff6f7d060ec20f6b0c651d77e9c9927cee4a20e4423f3a622b7134fe770f7cd9231569eb252e4f4a252f2e973d5a1e7feb8171fea9d00f62257937885ff193dd28e2fc1b3fbd9da3764a5c06e4ef51654d59fd8e4bb3155d443a4995c83c997c8965587322207957149636b0c1eeccd3d1f5e427ed8896cca3c5', + 'token_type' => 'bearer', + 'expires_in' => 2592000, + 'refresh_token_expires_in' => 31536000, + 'scope' => 'ads:read ads:write catalogs:read catalogs:write pins:read pins:write user_accounts:read user_accounts:write', + 'refresh_date' => 1685694065, + ); + Pinterest_For_Woocommerce()::save_token_data( $token_data ); + $info_data = array ( + 'advertiser_id' => '549765662491', + 'tag_id' => '2613286171854', + 'merchant_id' => '1479839719476', + 'clientHash' => 'MTQ4NjE3MzpkNWJjNTM4ZmVhMTZhYzIwMmZiNDZhMTFjMGNkZGVmNzFhOWU1YWY0', + ); + Pinterest_For_Woocommerce()::save_connection_info_data( $info_data ); + + // API Request filters to stub Pinterest API requests. + $this->create_commerce_integration_request_stub(); + $this->get_account_info_request_stub(); + $this->get_user_websites_request_stub(); + $this->get_linked_businesses_request_stub(); + + add_action( 'pinterest_for_woocommerce_token_saved', array( Pinterest_For_Woocommerce::class, 'set_default_settings' ) ); + add_action( 'pinterest_for_woocommerce_token_saved', array( Pinterest_For_Woocommerce::class, 'create_commerce_integration' ) ); + add_action( 'pinterest_for_woocommerce_token_saved', array( Pinterest_For_Woocommerce::class, 'update_account_data' ) ); + add_action( 'pinterest_for_woocommerce_token_saved', array( Pinterest_For_Woocommerce::class, 'update_linked_businesses' ) ); + + do_action( 'pinterest_for_woocommerce_token_saved' ); + + $settings = Pinterest_For_Woocommerce::get_settings( true ); + + $this->assertEquals( + array( + 'account_data' => array( + 'id' => '6491114367052267731', + 'username' => 'dmytromaksiuta1', + 'full_name' => '', + 'image_medium_url' => 'https://i.pinimg.com/600x600_R/42/f5/36/42f5364f737aff4749a8e9046510828f.jpg', + 'is_any_website_verified' => true, + 'is_billing_setup' => false, + 'is_partner' => true, + 'coupon_redeem_info' => array( + 'redeem_status' => false, + ), + 'verified_user_websites' => array( + 'wordpress.dima.works', + 'pinterest.dima.works' + ), + ), + 'track_conversions' => true, + 'enhanced_match_support' => true, + 'automatic_enhanced_match_support' => true, + 'save_to_pinterest' => true, + 'rich_pins_on_posts' => true, + 'rich_pins_on_products' => true, + 'product_sync_enabled' => true, + 'enable_debug_logging' => false, + 'erase_plugin_data' => false, + 'tracking_advertiser' => '549765662491', + 'tracking_tag' => '2613286171854', + ), + $settings + ); + } + + private function create_commerce_integration_request_stub() { + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) { + if ( 'https://api.pinterest.com/v5/integrations/commerce' === $url ) { + $response = array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array ( + 'id' => '6491114367052267731', + 'external_business_id' => 'wordpresspinterest-6479a6713160b', + 'connected_merchant_id' => '1479839719476', + 'connected_user_id' => '1144266355231574943', + 'connected_advertiser_id' => '549765662491', + 'connected_tag_id' => '2613286171854', + 'connected_lba_id' => '', + 'partner_access_token_expiry' => 0, + 'partner_refresh_token_expiry' => 0, + 'scopes' => '', + 'created_timestamp' => 1685694065000, + 'updated_timestamp' => 1685694065000, + 'additional_id_1' => '', + 'partner_metadata' => '', + ) + ), + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + 'cookies' => array(), + 'filename' => '', + ); + } + + return $response; + }, + 10, + 3 + ); + } + + private function get_account_info_request_stub() { + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) { + if ( 'https://api.pinterest.com/v5/user_account' === $url ) { + $response = array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + 'username' => 'dmytromaksiuta1', + 'profile_image' => 'https://i.pinimg.com/600x600_R/42/f5/36/42f5364f737aff4749a8e9046510828f.jpg', + 'account_type' => 'BUSINESS', + ) + ), + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + 'cookies' => array(), + 'filename' => '', + ); + } + + return $response; + }, + 10, + 3 + ); + } + + private function get_user_websites_request_stub() { + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) { + if ( 'https://api.pinterest.com/v5/user_account/websites' === $url ) { + $response = array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + 'items' => array( + array( + 'status' => 'verified', + 'website' => 'wordpress.dima.works', + ), + array( + 'status' => 'verified', + 'website' => 'pinterest.dima.works', + ) + ), + ) + ), + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + 'cookies' => array(), + 'filename' => '', + ); + } + + return $response; + }, + 10, + 3 + ); + } + + private function get_linked_businesses_request_stub() { + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) { + if ( 'https://api.pinterest.com/v5/user_account/businesses' === $url ) { + $response = array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + array( + 'username' => 'dmytromaksiuta1', + 'image_small_url' => 'https://www.example.com/dj23454f53dfk2324.jpg', + 'image_medium_url' => 'https://www.example.com/dj23454f53dfk2324.jpg', + 'image_large_url' => 'https://www.example.com/dj23454f53dfk2324.jpg', + 'image_xlarge_url' => 'https://www.example.com/dj23454f53dfk2324.jpg' + ), + ) + ), + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + 'cookies' => array(), + 'filename' => '', + ); + } + + return $response; + }, + 10, + 3 + ); + } +} diff --git a/tests/Unit/Api/SyncSettingsTest.php b/tests/Unit/Api/SyncSettingsTest.php new file mode 100644 index 000000000..198ca46bd --- /dev/null +++ b/tests/Unit/Api/SyncSettingsTest.php @@ -0,0 +1,108 @@ +get_routes(); + $this->assertArrayHasKey( '/pinterest/v1/sync_settings', $routes ); + } + + /** + * Tests if the sync settings endpoints reject access. + * + * @return void + */ + public function test_sync_settings_endpoint_rejects_access() { + // 1. No authentication. + $request = new WP_REST_Request( 'GET', '/pinterest/v1/sync_settings' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 401, $response->get_status() ); + + // 2. No authorisation. + $user = $this->factory->user->create( array( 'role' => 'guest' ) ); + wp_set_current_user( $user ); + + $request = new WP_REST_Request( 'GET', '/pinterest/v1/sync_settings' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 403, $response->get_status() ); + } + + /** + * Tests endpoint returns settings synced with Pinterest side. + * + * @return void + */ + public function test_sync_settings_endpoint_returns_settings_synced_with_pinterest() { + $user = $this->factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $user ); + + Pinterest_For_Woocommerce::save_setting( 'automatic_enhanced_match_support', false ); + Pinterest_For_WooCommerce::save_setting( 'tracking_advertiser', 'ai-123456789' ); + Pinterest_For_WooCommerce::save_setting( 'tracking_tag', 'ti-123456789' ); + + add_filter( + 'pre_http_request', + function ( $response, $args, $url ) { + if ( 'https://api.pinterest.com/v5/ad_accounts/ai-123456789/conversion_tags/ti-123456789' === $url ) { + $response = array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + 'configs' => array( + 'aem_enabled' => true, + ), + ) + ), + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + 'cookies' => array(), + 'filename' => '', + ); + } + return $response; + }, + 10, + 3 + ); + + $request = new WP_REST_Request( 'GET', '/pinterest/v1/sync_settings' ); + $response = rest_get_server()->dispatch( $request ); + + [ + 'success' => $success, + 'synced_settings' => $synced_settings, + ] = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertTrue( $success ); + $this->assertTrue( $synced_settings['automatic_enhanced_match_support'] ); + $this->assertTrue( Pinterest_For_WooCommerce::get_setting( 'automatic_enhanced_match_support' ) ); + $this->assertEquals( + $response->get_data()['synced_settings']['last_synced_settings'], + Pinterest_For_WooCommerce::get_setting( 'last_synced_settings' ) + ); + } +} diff --git a/tests/Unit/PinterestForWoocommerceTest.php b/tests/Unit/PinterestForWoocommerceTest.php index 02f0017ff..2331c4c60 100644 --- a/tests/Unit/PinterestForWoocommerceTest.php +++ b/tests/Unit/PinterestForWoocommerceTest.php @@ -9,6 +9,34 @@ class PinterestForWoocommerceTest extends WP_UnitTestCase { + /** + * Tests default settings are set. + * + * @return void + */ + public function test_set_default_settings() { + // Make sure settings are empty. + Pinterest_For_Woocommerce::save_settings( array() ); + + Pinterest_For_Woocommerce::set_default_settings(); + + $settings = Pinterest_For_Woocommerce::get_settings( true ); + $this->assertEquals( + array( + 'track_conversions' => true, + 'enhanced_match_support' => true, + 'automatic_enhanced_match_support' => true, + 'save_to_pinterest' => true, + 'rich_pins_on_posts' => true, + 'rich_pins_on_products' => true, + 'product_sync_enabled' => true, + 'enable_debug_logging' => false, + 'erase_plugin_data' => false, + ), + $settings + ); + } + /** * Test of the plugin has refresh token action initialised. * From bfbcbe8276b8d663213614c7fe0a81e54f73b5de Mon Sep 17 00:00:00 2001 From: Dima Date: Fri, 2 Jun 2023 19:31:56 +0300 Subject: [PATCH 24/36] Fix js and php lint errors. --- assets/source/setup-guide/app/steps/ClaimWebsite.js | 1 - class-pinterest-for-woocommerce.php | 13 +------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/assets/source/setup-guide/app/steps/ClaimWebsite.js b/assets/source/setup-guide/app/steps/ClaimWebsite.js index 12b85fb70..e298f7661 100644 --- a/assets/source/setup-guide/app/steps/ClaimWebsite.js +++ b/assets/source/setup-guide/app/steps/ClaimWebsite.js @@ -101,7 +101,6 @@ const ClaimWebsite = ( { view } ) => { const [ status, setStatus ] = useState( STATUS.IDLE ); const [ reqError, setReqError ] = useState(); const isDomainVerified = useSettingsSelect( 'isDomainVerified' ); - const setAppSettings = useSettingsDispatch( view === 'wizard' ); const createNotice = useCreateNotice(); const pfwSettings = wcSettings.pinterest_for_woocommerce; diff --git a/class-pinterest-for-woocommerce.php b/class-pinterest-for-woocommerce.php index a4616962d..eff8ac590 100644 --- a/class-pinterest-for-woocommerce.php +++ b/class-pinterest-for-woocommerce.php @@ -828,17 +828,6 @@ public function maybe_inject_verification_code() { } } - public static function unverify_website() { - var_export( 'Unverify website.' ); - $website = wp_parse_url( get_home_url() )['host']; - $response = Pinterest\API\APIV5::make_request( - "user_account/websites?website={$website}", - 'DELETE' - ); - var_export( $response ); - exit; - } - /** * Connects WC to Pinterest. * @@ -1353,7 +1342,7 @@ public static function is_business_connected() { public static function is_domain_verified(): bool { $account_data = self::get_setting( 'account_data' ); $verified_domains = $account_data['verified_user_websites'] ?? array(); - return in_array( wp_parse_url( get_home_url() )['host'] ?? '' , $verified_domains ); + return in_array( wp_parse_url( get_home_url() )['host'] ?? '', $verified_domains ); } /** From a77ec82d7bc6031baab2ffebc6064cf4d63d2592 Mon Sep 17 00:00:00 2001 From: Dima Date: Fri, 2 Jun 2023 19:34:00 +0300 Subject: [PATCH 25/36] Fix js lint errors. --- assets/source/setup-guide/app/steps/ClaimWebsite.js | 1 - 1 file changed, 1 deletion(-) diff --git a/assets/source/setup-guide/app/steps/ClaimWebsite.js b/assets/source/setup-guide/app/steps/ClaimWebsite.js index e298f7661..3ee24ee73 100644 --- a/assets/source/setup-guide/app/steps/ClaimWebsite.js +++ b/assets/source/setup-guide/app/steps/ClaimWebsite.js @@ -30,7 +30,6 @@ import UrlInputControl from '../components/UrlInputControl'; import StatusLabel from '../components/StatusLabel'; import { useSettingsSelect, - useSettingsDispatch, useCreateNotice, } from '../helpers/effects'; import documentationLinkProps from '../helpers/documentation-link-props'; From 73385b3c96a70550edc6a651a93d770fa1361dcd Mon Sep 17 00:00:00 2001 From: Dima Date: Fri, 2 Jun 2023 19:42:34 +0300 Subject: [PATCH 26/36] Update test according to new domain verification logic changes. --- tests/Unit/TasksTest.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/Unit/TasksTest.php b/tests/Unit/TasksTest.php index 492ecbbfc..2d26b67a7 100644 --- a/tests/Unit/TasksTest.php +++ b/tests/Unit/TasksTest.php @@ -5,6 +5,7 @@ namespace Automattic\WooCommerce\Pinterest\Tests\Unit; +use Pinterest_For_Woocommerce; use \WP_UnitTestCase; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskLists; use Automattic\WooCommerce\Pinterest\Admin\Tasks\Onboarding; @@ -49,6 +50,13 @@ public function test_add_onboarding_task_added() { * Tasks to assert the completion of the Onboarding task. */ public function test_onboarding_task_completed() { + add_filter( + 'home_url', + function() { + return 'https://somedomain.com/'; + } + ); + // Assert that the task is not completed. $task = TaskLists::get_task( 'setup-pinterest' ); $is_complete = $task->is_complete(); @@ -58,18 +66,19 @@ public function test_onboarding_task_completed() { // Setup complete data. $account_data = array( 'is_any_website_verified' => true, + 'verified_user_websites' => array( 'somedomain.com' ), 'is_partner' => true, ); - \Pinterest_For_Woocommerce::save_setting( 'account_data', $account_data ); - \Pinterest_For_Woocommerce::save_token_data( + Pinterest_For_Woocommerce::save_setting( 'account_data', $account_data ); + Pinterest_For_Woocommerce::save_token_data( array( 'access_token' => 'some-fake-access-token', ) ); - \Pinterest_For_Woocommerce::save_setting( 'tracking_tag', true ); + Pinterest_For_Woocommerce::save_setting( 'tracking_tag', true ); // Assert that the task is completed. $is_complete = $task->is_complete(); $this->assertTrue( $is_complete, 'Cannot assert task completion.' ); } -} \ No newline at end of file +} From 003ab9f0ff8b0ebbbf81068e6517810579f4a079 Mon Sep 17 00:00:00 2001 From: Dima Date: Fri, 2 Jun 2023 19:44:39 +0300 Subject: [PATCH 27/36] Fix js lint error. --- assets/source/setup-guide/app/steps/ClaimWebsite.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/assets/source/setup-guide/app/steps/ClaimWebsite.js b/assets/source/setup-guide/app/steps/ClaimWebsite.js index 3ee24ee73..57112e8ef 100644 --- a/assets/source/setup-guide/app/steps/ClaimWebsite.js +++ b/assets/source/setup-guide/app/steps/ClaimWebsite.js @@ -28,10 +28,7 @@ import StepHeader from '../components/StepHeader'; import StepOverview from '../components/StepOverview'; import UrlInputControl from '../components/UrlInputControl'; import StatusLabel from '../components/StatusLabel'; -import { - useSettingsSelect, - useCreateNotice, -} from '../helpers/effects'; +import { useSettingsSelect, useCreateNotice } from '../helpers/effects'; import documentationLinkProps from '../helpers/documentation-link-props'; import { getNewPath } from '@woocommerce/navigation'; // eslint-disable-line From e9bce7379fcf046452fb4441654453aabaa0cdb4 Mon Sep 17 00:00:00 2001 From: Dima Date: Sat, 3 Jun 2023 17:46:37 +0300 Subject: [PATCH 28/36] Refactor domain verification endpoint, reduce the number of calls to pinterest api and update the setting depending on the domain verification Pinterest endpoint results. Add domain verification plugin's endpoint unit test. --- src/API/DomainVerification.php | 11 +- tests/Unit/Api/DomainVerificationTest.php | 155 ++++++++++++++++++++++ 2 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 tests/Unit/Api/DomainVerificationTest.php diff --git a/src/API/DomainVerification.php b/src/API/DomainVerification.php index 20198a94f..9a3b7834c 100644 --- a/src/API/DomainVerification.php +++ b/src/API/DomainVerification.php @@ -57,15 +57,19 @@ public function __construct() { */ public function maybe_handle_verification() { try { - $result = array(); + $result = array(); + $account_data = Pinterest_For_Woocommerce()::get_setting( 'account_data', true ); if ( ! Pinterest_For_Woocommerce::is_domain_verified() ) { $domain_verification_data = APIV5::domain_verification_data(); Pinterest_For_Woocommerce()::save_data( 'verification_data', $domain_verification_data ); - $parsed_website = wp_parse_url( get_home_url() ); $result = APIV5::domain_metatag_verification_request( $parsed_website['host'] . $parsed_website['path'] ); + if ( 'success' === $result['status'] ) { + $account_data['verified_user_websites'][] = $result['website']; + $account_data['is_any_website_verified'] = 0 < count( $account_data['verified_user_websites'] ); + Pinterest_For_Woocommerce()::save_setting( 'account_data', $account_data ); + } } - $account_data = Pinterest_For_Woocommerce()::update_account_data(); return array_merge( $result, array( 'account_data' => $account_data ) ); } catch ( PinterestApiException $th ) { return new WP_Error( @@ -77,7 +81,6 @@ public function maybe_handle_verification() { ) ); } - // return self::trigger_domain_verification();. } diff --git a/tests/Unit/Api/DomainVerificationTest.php b/tests/Unit/Api/DomainVerificationTest.php new file mode 100644 index 000000000..654f18b5c --- /dev/null +++ b/tests/Unit/Api/DomainVerificationTest.php @@ -0,0 +1,155 @@ +get_routes(); + $this->assertArrayHasKey( '/pinterest/v1/domain_verification', $routes ); + } + + /** + * Tests if the domain verification endpoint rejects access. + * + * @return void + */ + public function test_domain_verification_endpoint_rejects_access() { + // 1. No authentication. + $request = new WP_REST_Request( 'POST', '/pinterest/v1/domain_verification' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 401, $response->get_status() ); + + // 2. No authorisation. + $user = $this->factory->user->create( array( 'role' => 'guest' ) ); + wp_set_current_user( $user ); + + $request = new WP_REST_Request( 'POST', '/pinterest/v1/domain_verification' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 403, $response->get_status() ); + } + + /** + * Tests if the domain verification endpoint returns domain verification status and saves appropriate settings. + * + * @return void + */ + public function test_domain_verification_endpoint_verifies_a_domain() { + $user = $this->factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $user ); + + // Partial mock of account_data data. + Pinterest_For_Woocommerce()::save_setting( + 'account_data', + array( + 'username' => 'dmytromaksiuta1', + 'id' => '1234567890123456789', + 'is_partner' => true, + 'is_billing_setup' => false, + 'verified_user_websites' => array(), + 'is_any_website_verified' => false, + ) + ); + + add_filter( 'home_url', fn () => 'https://mysite.test/' ); + + add_filter( + 'pre_http_request', + function ( $response, $parsed_args, $url ) { + if ( 'https://api.pinterest.com/v5/user_account/websites/verification' === $url ) { + $response = array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + 'verification_code' => 'e1edcc1a43976c646367e9c6c9a9b7b6', + 'dns_txt_record' => 'pinterest-site-verification=e1edcc1a43976c646367e9c6c9a9b7b6', + 'metatag' => '', + 'filename' => 'pinterest-e1edc.html', + 'file_content' => 'string', + ) + ), + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + 'cookies' => array(), + 'filename' => '', + ); + } + if ( 'https://api.pinterest.com/v5/user_account/websites' === $url ) { + $response = array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + 'website' => 'mysite.test', + 'status' => 'success', + 'verified_at' => '2022-12-14T21:03:01.602000' + ) + ), + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + 'cookies' => array(), + 'filename' => '', + ); + } + return $response; + }, + 10, + 3 + ); + + $request = new WP_REST_Request( 'POST', '/pinterest/v1/domain_verification' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( + array( + 'website' => 'mysite.test', + 'status' => 'success', + 'verified_at' => '2022-12-14T21:03:01.602000', + 'account_data' => array( + 'username' => 'dmytromaksiuta1', + 'id' => '1234567890123456789', + 'is_partner' => true, + 'is_billing_setup' => false, + 'verified_user_websites' => array( 'mysite.test' ), + 'is_any_website_verified' => true, + ) + ), + $response->get_data() + ); + $this->assertEquals( + array( + 'username' => 'dmytromaksiuta1', + 'id' => '1234567890123456789', + 'is_partner' => true, + 'is_billing_setup' => false, + 'verified_user_websites' => array( 'mysite.test' ), + 'is_any_website_verified' => true, + ), + Pinterest_For_Woocommerce()::get_setting( 'account_data', true ) + ); + } +} From 87c8664d963eb8c4d7139c002ef565d72bf85e25 Mon Sep 17 00:00:00 2001 From: Dima Date: Sat, 3 Jun 2023 17:49:19 +0300 Subject: [PATCH 29/36] Fix syntax for earlier php versions. --- tests/Unit/Api/DomainVerificationTest.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/Unit/Api/DomainVerificationTest.php b/tests/Unit/Api/DomainVerificationTest.php index 654f18b5c..90981b087 100644 --- a/tests/Unit/Api/DomainVerificationTest.php +++ b/tests/Unit/Api/DomainVerificationTest.php @@ -67,7 +67,12 @@ public function test_domain_verification_endpoint_verifies_a_domain() { ) ); - add_filter( 'home_url', fn () => 'https://mysite.test/' ); + add_filter( + 'home_url', + function () { + return 'https://mysite.test/'; + } + ); add_filter( 'pre_http_request', From 6cba67445415374573a4063f8c094b0eb03bbef4 Mon Sep 17 00:00:00 2001 From: Dima Date: Sat, 3 Jun 2023 18:13:51 +0300 Subject: [PATCH 30/36] Add tagowners plugin rest api endpoint test. --- src/API/Advertisers.php | 14 ++-- tests/Unit/Api/AdvertisersTest.php | 126 +++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 tests/Unit/Api/AdvertisersTest.php diff --git a/src/API/Advertisers.php b/src/API/Advertisers.php index 12ccfe568..c6e593a42 100644 --- a/src/API/Advertisers.php +++ b/src/API/Advertisers.php @@ -48,12 +48,16 @@ public function __construct() { public function get_advertisers( WP_REST_Request $request ) { try { $advertisers = APIV5::get_advertisers(); - $terms_agreed = $request->has_param( 'terms_agreed' ) ? (int) $request->get_param( 'terms_agreed' ) : false; - if ( empty( $advertisers['items'] ) && ! empty( $terms_agreed ) ) { - $advertiser = Base::create_advertiser( $terms_agreed ); - $advertisers['data'] = 'success' === $advertiser['status'] ? array( $advertiser['data'] ) : array(); - } + /* + * With the new Pinterest on-boarding popup we do not need to create and advertiser. We will always have it. + * + * $terms_agreed = $request->has_param( 'terms_agreed' ) ? (int) $request->get_param( 'terms_agreed' ) : false; + * if ( empty( $advertisers['items'] ) && ! empty( $terms_agreed ) ) { + * $advertiser = Base::create_advertiser( $terms_agreed ); + * $advertisers['data'] = 'success' === $advertiser['status'] ? array( $advertiser['data'] ) : array(); + * } + */ return array( 'advertisers' => array_map( diff --git a/tests/Unit/Api/AdvertisersTest.php b/tests/Unit/Api/AdvertisersTest.php new file mode 100644 index 000000000..32b1ef4a6 --- /dev/null +++ b/tests/Unit/Api/AdvertisersTest.php @@ -0,0 +1,126 @@ +get_routes(); + $this->assertArrayHasKey( '/pinterest/v1/tagowners', $routes ); + } + + /** + * Tests if advertisers endpoints rejects access. + * + * @return void + */ + public function test_advertisers_endpoint_rejects_access() { + // 1. No authentication. + $request = new WP_REST_Request( 'GET', '/pinterest/v1/tagowners' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 401, $response->get_status() ); + + // 2. No authorisation. + $user = $this->factory->user->create( array( 'role' => 'guest' ) ); + wp_set_current_user( $user ); + + $request = new WP_REST_Request( 'GET', '/pinterest/v1/tagowners' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 403, $response->get_status() ); + } + + /** + * Tests advertisers endpoint returns the list of advertisers. + * + * @return void + */ + public function test_advertisers_endpoint_returns_the_list_of_advertisers() { + $user = $this->factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $user ); + + add_filter( + 'pre_http_request', + function ( $response, $args, $url ) { + if ( 'https://api.pinterest.com/v5/ad_accounts' === $url ) { + $response = array( + 'headers' => array( + 'content-type' => 'application/json', + ), + 'body' => json_encode( + array( + 'items' => array( + array( + 'id' => '1234567890', + 'name' => 'Test Advertiser 0', + 'owner' => array( + 'username' => 'username_007', + ), + 'country' => 'US', + 'currency' => 'USD', + 'permissions' => 'some-permissions', + ), + array( + 'id' => '1234567891', + 'name' => 'Test Advertiser 1', + 'owner' => array( + 'username' => 'username_008', + ), + 'country' => 'US', + 'currency' => 'USD', + 'permissions' => 'some-permissions', + ), + ), + ) + ), + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + 'cookies' => array(), + 'filename' => '', + ); + } + return $response; + }, + 10, + 3 + ); + + $request = new WP_REST_Request( 'GET', '/pinterest/v1/tagowners' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( + array( + 'advertisers' => array( + array( + 'id' => '1234567890', + 'name' => 'Test Advertiser 0', + ), + array( + 'id' => '1234567891', + 'name' => 'Test Advertiser 1', + ), + ), + ), + $response->get_data() + ); + } +} From 77de5e4b4f2f61ab3c7c62fc579a139bcc074136 Mon Sep 17 00:00:00 2001 From: Dima Date: Sat, 3 Jun 2023 18:38:53 +0300 Subject: [PATCH 31/36] Fix claim website screen complete setup button rendering. --- assets/source/setup-guide/app/steps/ClaimWebsite.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/assets/source/setup-guide/app/steps/ClaimWebsite.js b/assets/source/setup-guide/app/steps/ClaimWebsite.js index 57112e8ef..03905b7d5 100644 --- a/assets/source/setup-guide/app/steps/ClaimWebsite.js +++ b/assets/source/setup-guide/app/steps/ClaimWebsite.js @@ -283,11 +283,9 @@ const ClaimWebsite = ( { view } ) => { ) } - { view === 'wizard' && ( + { view === 'wizard' && status !== STATUS.IDLE && (

- { status !== STATUS.IDLE && ( - - ) } +
) } From 691fcdb3d391ca47a08fd90567a2d7f49ee7b8e5 Mon Sep 17 00:00:00 2001 From: Dima Date: Tue, 6 Jun 2023 19:44:43 +0300 Subject: [PATCH 32/36] Change claim website UI a bit. --- .../setup-guide/app/steps/ClaimWebsite.js | 56 +++++++++---------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/assets/source/setup-guide/app/steps/ClaimWebsite.js b/assets/source/setup-guide/app/steps/ClaimWebsite.js index 03905b7d5..1ea47c956 100644 --- a/assets/source/setup-guide/app/steps/ClaimWebsite.js +++ b/assets/source/setup-guide/app/steps/ClaimWebsite.js @@ -9,7 +9,6 @@ import { createInterpolateElement, } from '@wordpress/element'; import apiFetch from '@wordpress/api-fetch'; -import { Spinner } from '@woocommerce/components'; import { Button, Card, @@ -180,7 +179,8 @@ const ClaimWebsite = ( { view } ) => { : handleClaimWebsite } > - { buttonLabels[ status ] } + { buttonLabels[ status ] ?? + __( 'Verifying…', 'pinterest-for-woocommerce' ) } ); }; @@ -251,39 +251,33 @@ const ClaimWebsite = ( { view } ) => {
- { undefined !== isDomainVerified ? ( - - - { __( - 'Verify your domain to claim your website', - 'pinterest-for-woocommerce' - ) } - - - { __( - 'This will allow access to analytics for the Pins you publish from your site, the analytics on Pins that other people create from your site, and let people know where they can find more of your content.', - 'pinterest-for-woocommerce' - ) } - + + + { __( + 'Verify your domain to claim your website', + 'pinterest-for-woocommerce' + ) } + + + { __( + 'This will allow access to analytics for the Pins you publish from your site, the analytics on Pins that other people create from your site, and let people know where they can find more of your content.', + 'pinterest-for-woocommerce' + ) } + - - - - + + + + - - - ) : ( - - - - ) } + + - { view === 'wizard' && status !== STATUS.IDLE && ( + { view === 'wizard' && (
From 3399856e2062e327398fa86c8760fad56b31a740 Mon Sep 17 00:00:00 2001 From: Dima Date: Tue, 6 Jun 2023 22:21:49 +0300 Subject: [PATCH 33/36] Adjust domain verification UI in case of domain is pending verification still, not to repeat domain verification requests infinitely and provide tons of notices. --- .../setup-guide/app/steps/ClaimWebsite.js | 20 ++++++-- i18n/languages/pinterest-for-woocommerce.pot | 47 ++++++++++--------- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/assets/source/setup-guide/app/steps/ClaimWebsite.js b/assets/source/setup-guide/app/steps/ClaimWebsite.js index 1ea47c956..7f857e82e 100644 --- a/assets/source/setup-guide/app/steps/ClaimWebsite.js +++ b/assets/source/setup-guide/app/steps/ClaimWebsite.js @@ -100,9 +100,12 @@ const ClaimWebsite = ( { view } ) => { const pfwSettings = wcSettings.pinterest_for_woocommerce; useEffect( () => { - // If domain is not verified and verification status is not pending nor success - start verification. + // If domain is not verified and verification status is not pending, error nor success - start verification. if ( - ! Object.values( LABEL_STATUS ).includes( status ) && + ! Object.values( { + ...LABEL_STATUS, + ERROR: STATUS.ERROR, + } ).includes( status ) && ! isDomainVerified ) { handleClaimWebsite(); @@ -158,7 +161,18 @@ const ClaimWebsite = ( { view } ) => { const text = buttonLabels[ status ]; - return ; + if ( + Object.values( { + ...LABEL_STATUS, + IDLE: STATUS.IDLE, + } ).includes( status ) + ) { + return ; + } + + return ( +