Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add site_id to the SFCC datasource #353

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 29 additions & 22 deletions inc/Integrations/SalesforceB2C/Auth/SalesforceB2CAuth.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,35 @@ class SalesforceB2CAuth {
* @param string $organization_id The organization ID for the data source.
* @param string $client_id The client ID (a version 4 UUID).
* @param string $client_secret The client secret.
* @param string $site_id The storefront site ID.
* @return string|WP_Error The token or an error.
*/
public static function generate_token(
string $endpoint,
string $organization_id,
string $client_id,
string $client_secret
string $client_secret,
// ToDo: Remove the optional value once existing SFCC data sources have been migrated.
?string $site_id = 'RefArchGlobal',
): string|WP_Error {
$saved_access_token = self::get_saved_access_token( $organization_id, $client_id );
$saved_access_token = self::get_saved_access_token( $organization_id, $client_id, $site_id );

if ( null !== $saved_access_token ) {
return $saved_access_token;
}

$access_token = null;

$saved_refresh_token = self::get_saved_refresh_token( $organization_id, $client_id );
$saved_refresh_token = self::get_saved_refresh_token( $organization_id, $client_id, $site_id );
if ( null !== $saved_refresh_token ) {
$access_token = self::get_token_using_refresh_token( $saved_refresh_token, $client_id, $client_secret, $endpoint, $organization_id );
$access_token = self::get_token_using_refresh_token( $saved_refresh_token, $client_id, $client_secret, $endpoint, $organization_id, $site_id );
}

if ( null !== $access_token && ! is_wp_error( $access_token ) ) {
return $access_token;
}

$access_token = self::get_token_using_client_credentials( $client_id, $client_secret, $endpoint, $organization_id );
$access_token = self::get_token_using_client_credentials( $client_id, $client_secret, $endpoint, $organization_id, $site_id );
return $access_token;
}

Expand All @@ -54,14 +57,16 @@ public static function get_token_using_client_credentials(
string $client_secret,
string $endpoint,
string $organization_id,
// ToDo: Remove the optional value once existing SFCC data sources have been migrated.
?string $site_id = 'RefArchGlobal',
): WP_Error|string {
$client_auth_url = sprintf( '%s/shopper/auth/v1/organizations/%s/oauth2/token', $endpoint, $organization_id );
$client_credentials = base64_encode( sprintf( '%s:%s', $client_id, $client_secret ) );

$client_auth_response = wp_remote_post($client_auth_url, [
'body' => [
'grant_type' => 'client_credentials',
'channel_id' => 'RefArch',
'channel_id' => $site_id,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I noticed this was different than the site_id hardcoded elsewhere of RefArchGlobal so I've corrected this.

],
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
Expand Down Expand Up @@ -90,11 +95,11 @@ public static function get_token_using_client_credentials(

$access_token = $response_data['access_token'];
$access_token_expires_in = $response_data['expires_in'];
self::save_access_token( $access_token, $access_token_expires_in, $organization_id, $client_id );
self::save_access_token( $access_token, $access_token_expires_in, $organization_id, $client_id, $site_id );

$refresh_token = $response_data['refresh_token'];
$refresh_token_expires_in = $response_data['refresh_token_expires_in'];
self::save_refresh_token( $refresh_token, $refresh_token_expires_in, $organization_id, $client_id );
self::save_refresh_token( $refresh_token, $refresh_token_expires_in, $organization_id, $client_id, $site_id );

return $access_token;
}
Expand All @@ -107,6 +112,8 @@ public static function get_token_using_refresh_token(
string $client_secret,
string $endpoint,
string $organization_id,
// ToDo: Remove the optional value once existing SFCC data sources have been migrated.
?string $site_id = 'RefArchGlobal',
): string|WP_Error {
$client_auth_url = sprintf( '%s/shopper/auth/v1/organizations/%s/oauth2/token', $endpoint, $organization_id );

Expand All @@ -117,7 +124,7 @@ public static function get_token_using_refresh_token(
'body' => [
'grant_type' => 'refresh_token',
'refresh_token' => $refresh_token,
'channel_id' => 'RefArch',
'channel_id' => $site_id,
],
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
Expand All @@ -144,7 +151,7 @@ public static function get_token_using_refresh_token(

$access_token = $response_data['access_token'];
$access_token_expires_in = $response_data['expires_in'];
self::save_access_token( $access_token, $access_token_expires_in, $organization_id, $client_id );
self::save_access_token( $access_token, $access_token_expires_in, $organization_id, $client_id, $site_id );

// No need to save the refresh token, as it stays the same until we perform a top-level authentication

Expand All @@ -153,7 +160,7 @@ public static function get_token_using_refresh_token(

// Access token cache management

private static function save_access_token( string $access_token, int $expires_in, string $organization_id, string $client_id ): void {
private static function save_access_token( string $access_token, int $expires_in, string $organization_id, string $client_id, string $site_id ): void {
// Expires 10 seconds early as a buffer for request time and drift
$access_token_expires_in = $expires_in - 10;

Expand All @@ -162,7 +169,7 @@ private static function save_access_token( string $access_token, int $expires_in
'expires_at' => time() + $access_token_expires_in,
];

$access_token_cache_key = self::get_access_token_key( $organization_id, $client_id );
$access_token_cache_key = self::get_access_token_key( $organization_id, $client_id, $site_id );

wp_cache_set(
$access_token_cache_key,
Expand All @@ -173,8 +180,8 @@ private static function save_access_token( string $access_token, int $expires_in
);
}

private static function get_saved_access_token( string $organization_id, string $client_id ): ?string {
$access_token_cache_key = self::get_access_token_key( $organization_id, $client_id );
private static function get_saved_access_token( string $organization_id, string $client_id, string $site_id ): ?string {
$access_token_cache_key = self::get_access_token_key( $organization_id, $client_id, $site_id );

$saved_access_token = wp_cache_get( $access_token_cache_key, 'oauth-tokens' );

Expand All @@ -193,14 +200,14 @@ private static function get_saved_access_token( string $organization_id, string
return $access_token;
}

private static function get_access_token_key( string $organization_id, string $client_id ): string {
$cache_key_suffix = hash( 'sha256', sprintf( '%s-%s', $organization_id, $client_id ) );
private static function get_access_token_key( string $organization_id, string $client_id, string $site_id ): string {
$cache_key_suffix = hash( 'sha256', sprintf( '%s-%s-%s', $organization_id, $client_id, $site_id ) );
return sprintf( 'salesforce_b2c_access_token_%s', $cache_key_suffix );
}

// Refresh token cache management

private static function save_refresh_token( string $refresh_token, int $expires_in, string $organization_id, string $client_id ): void {
private static function save_refresh_token( string $refresh_token, int $expires_in, string $organization_id, string $client_id, string $site_id ): void {
// Expires 10 seconds early as a buffer for request time and drift
$refresh_token_expires_in = $expires_in - 10;

Expand All @@ -209,7 +216,7 @@ private static function save_refresh_token( string $refresh_token, int $expires_
'expires_at' => time() + $refresh_token_expires_in,
];

$refresh_token_cache_key = self::get_refresh_token_cache_key( $organization_id, $client_id );
$refresh_token_cache_key = self::get_refresh_token_cache_key( $organization_id, $client_id, $site_id );

wp_cache_set(
$refresh_token_cache_key,
Expand All @@ -220,8 +227,8 @@ private static function save_refresh_token( string $refresh_token, int $expires_
);
}

private static function get_saved_refresh_token( string $organization_id, string $client_id ): ?string {
$refresh_token_cache_key = self::get_refresh_token_cache_key( $organization_id, $client_id );
private static function get_saved_refresh_token( string $organization_id, string $client_id, string $site_id ): ?string {
$refresh_token_cache_key = self::get_refresh_token_cache_key( $organization_id, $client_id, $site_id );

$saved_refresh_token = wp_cache_get( $refresh_token_cache_key, 'oauth-tokens' );

Expand All @@ -240,8 +247,8 @@ private static function get_saved_refresh_token( string $organization_id, string
return $refresh_token;
}

private static function get_refresh_token_cache_key( string $organization_id, string $client_id ): string {
$cache_key_suffix = hash( 'sha256', sprintf( '%s-%s', $organization_id, $client_id ) );
private static function get_refresh_token_cache_key( string $organization_id, string $client_id, string $site_id ): string {
$cache_key_suffix = hash( 'sha256', sprintf( '%s-%s-%s', $organization_id, $client_id, $site_id ) );
return sprintf( 'salesforce_b2c_refresh_token_%s', $cache_key_suffix );
}
}
2 changes: 2 additions & 0 deletions inc/Integrations/SalesforceB2C/SalesforceB2CDataSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ protected static function get_service_config_schema(): array {
'enable_blocks' => Types::nullable( Types::boolean() ),
'organization_id' => Types::string(),
'shortcode' => Types::string(),
// ToDo: Remove the nullable, once existing SFCC data sources have been migrated.
'site_id' => Types::nullable( Types::string() ),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is set to nullable only until the existing test sites are migrated. After that, the nullable part can be dropped.

Same thing as below, it's an interim fix to add the site_id in place with the intention that the internal plan to add migration would negate this in the future.

] );
}

Expand Down
25 changes: 19 additions & 6 deletions inc/Integrations/SalesforceB2C/SalesforceB2CIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@ public static function register_blocks(): void {

foreach ( $data_source_configs as $config ) {
$data_source = SalesforceB2CDataSource::from_array( $config );

if ( false === ( $config['service_config']['enable_blocks'] ?? true ) ) {
continue;
}


// ToDo: Remove this once existing SFCC data sources have been migrated.
if ( empty( $config['service_config']['site_id'] ) ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is present to stop existing test sites from breaking. It adds the previous value as a default value. Once these test sites have been migrated, this can be removed.

I've added this as an interim fix to add the site_id in place with the intention that the internal plan to add migration as a feature would prevent this down the line.

$config['service_config']['site_id'] = 'RefArchGlobal';
}

self::register_blocks_for_salesforce_data_source( $data_source );
}
}
Expand All @@ -31,12 +36,18 @@ private static function get_queries( SalesforceB2CDataSource $data_source ): arr
$base_endpoint = $data_source->get_endpoint();
$service_config = $data_source->to_array()['service_config'];

// ToDo: Remove this once existing SFCC data sources have been migrated.
if ( empty( $service_config['site_id'] ) ) {
$service_config['site_id'] = 'RefArchGlobal';
}

$get_request_headers = function () use ( $base_endpoint, $service_config ): array|WP_Error {
$access_token = SalesforceB2CAuth::generate_token(
$base_endpoint,
$service_config['organization_id'],
$service_config['client_id'],
$service_config['client_secret']
$service_config['client_secret'],
$service_config['site_id']
);
$request_headers = [ 'Content-Type' => 'application/json' ];

Expand All @@ -52,10 +63,11 @@ private static function get_queries( SalesforceB2CDataSource $data_source ): arr
'data_source' => $data_source,
'endpoint' => function ( array $input_variables ) use ( $base_endpoint, $service_config ): string {
return sprintf(
'%s/product/shopper-products/v1/organizations/%s/products/%s?siteId=RefArchGlobal',
'%s/product/shopper-products/v1/organizations/%s/products/%s?siteId=%s',
$base_endpoint,
$service_config['organization_id'],
$input_variables['product_id']
$input_variables['product_id'],
$service_config['site_id']
);
},
'input_schema' => [
Expand Down Expand Up @@ -105,9 +117,10 @@ private static function get_queries( SalesforceB2CDataSource $data_source ): arr
'data_source' => $data_source,
'endpoint' => function ( array $input_variables ) use ( $base_endpoint, $service_config ): string {
return sprintf(
'%s/search/shopper-search/v1/organizations/%s/product-search?siteId=RefArchGlobal&q=%s',
'%s/search/shopper-search/v1/organizations/%s/product-search?siteId=%s&q=%s',
$base_endpoint,
$service_config['organization_id'],
$service_config['site_id'],
urlencode( $input_variables['search'] )
);
},
Expand Down
32 changes: 28 additions & 4 deletions src/data-sources/salesforce-b2c/SalesforceB2CSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { TextControl } from '@wordpress/components';
import { useMemo } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

import { DataSourceForm } from '../components/DataSourceForm';
import { DataSourceForm } from '@/data-sources/components/DataSourceForm';
import PasswordInputControl from '@/data-sources/components/PasswordInputControl';
import { useDataSources } from '@/data-sources/hooks/useDataSources';
import {
SettingsComponentProps,
SalesforceB2CConfig,
SalesforceB2CServiceConfig,
SettingsComponentProps,
} from '@/data-sources/types';
import { useForm } from '@/hooks/useForm';
import SalesforceCommerceB2CIcon from '@/settings/icons/SalesforceCommerceB2CIcon';
Expand All @@ -30,8 +30,20 @@ export const SalesforceB2CSettings = ( {
} );

const shouldAllowSubmit = useMemo( () => {
return state.shortcode && state.organization_id && state.client_id && state.client_secret;
}, [ state.shortcode, state.organization_id, state.client_id, state.client_secret ] );
return (
state.site_id &&
state.shortcode &&
state.organization_id &&
state.client_id &&
state.client_secret
);
}, [
state.site_id,
state.shortcode,
state.organization_id,
state.client_id,
state.client_secret,
] );

const onSaveClick = async () => {
if ( ! validState ) {
Expand Down Expand Up @@ -86,6 +98,18 @@ export const SalesforceB2CSettings = ( {
__next40pxDefaultSize
/>

<TextControl
type="text"
label={ __( 'Site ID', 'remote-data-blocks' ) }
onChange={ siteId => {
handleOnChange( 'site_id', siteId ?? '' );
} }
value={ state.site_id ?? 'RefArchGlobal' }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Provided a default value similar to what we had before of RefArchGlobal. Figured since there isn't exactly an API to get a list of this, I've left it as a textbox with a helpful value.

help={ __( 'The site ID. Example: RefArchGlobal' ) }
autoComplete="off"
__next40pxDefaultSize
/>

<TextControl
type="text"
label={ __( 'Client ID', 'remote-data-blocks' ) }
Expand Down
1 change: 1 addition & 0 deletions src/data-sources/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export interface SalesforceB2CServiceConfig extends BaseServiceConfig {
organization_id: string;
client_id: string;
client_secret: string;
site_id: string;
}

export interface ShopifyServiceConfig extends BaseServiceConfig {
Expand Down
Loading