From f365a8f86f2d86daa428cd880fa8042422466475 Mon Sep 17 00:00:00 2001 From: Brian Shen Date: Mon, 16 Oct 2023 19:31:31 +0000 Subject: [PATCH] add authenticated account to customer account api --- .../hooks/authenticated-account.ts | 33 ++++++++++ .../customer-account/hooks/buyer-identity.ts | 18 ++--- .../surfaces/customer-account/hooks/index.ts | 4 ++ .../tests/authenticated-account.test.tsx | 66 +++++++++++++++++++ .../src/surfaces/customer-account/api.ts | 9 +-- .../api/order-status/order-status.ts | 10 ++- .../surfaces/customer-account/api/shared.ts | 40 +++++++++++ .../api/standard-api/standard-api.ts | 34 +++------- 8 files changed, 170 insertions(+), 44 deletions(-) create mode 100644 packages/ui-extensions-react/src/surfaces/customer-account/hooks/authenticated-account.ts create mode 100644 packages/ui-extensions-react/src/surfaces/customer-account/hooks/tests/authenticated-account.test.tsx diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/hooks/authenticated-account.ts b/packages/ui-extensions-react/src/surfaces/customer-account/hooks/authenticated-account.ts new file mode 100644 index 000000000..40fd8fc3f --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/hooks/authenticated-account.ts @@ -0,0 +1,33 @@ +import type { + Customer, + PurchasingCompany, + RenderExtensionTarget, +} from '@shopify/ui-extensions/customer-account'; + +import {useApi} from './api'; +import {useSubscription} from './subscription'; + +/** + * Returns the current authenticated `Customer`. + * + * The value is `undefined` if the customer hasn't authenticated yet. + */ +export function useAuthenticatedAccountCustomer< + Target extends RenderExtensionTarget, +>(): Customer | undefined { + const account = useApi().authenticatedAccount; + + return useSubscription(account.customer); +} + +/** + * Provides information about the company and its location the authenticated business customer. + * The value is `undefined` if a business customer isn't authenticated. + */ +export function useAuthenticatedAccountPurchasingCompany< + Target extends RenderExtensionTarget, +>(): PurchasingCompany | undefined { + const account = useApi().authenticatedAccount; + + return useSubscription(account.purchasingCompany); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/hooks/buyer-identity.ts b/packages/ui-extensions-react/src/surfaces/customer-account/hooks/buyer-identity.ts index 2cff30312..1aea5f001 100644 --- a/packages/ui-extensions-react/src/surfaces/customer-account/hooks/buyer-identity.ts +++ b/packages/ui-extensions-react/src/surfaces/customer-account/hooks/buyer-identity.ts @@ -1,8 +1,6 @@ import type { - Customer, + OrderStatusCustomer, OrderStatusPurchasingCompany, - PurchasingCompany as CustomerAccountPurchasingCompany, - RenderExtensionTarget, RenderOrderStatusExtensionTarget, } from '@shopify/ui-extensions/customer-account'; @@ -18,7 +16,7 @@ import {useSubscription} from './subscription'; */ export function useCustomer< Target extends RenderOrderStatusExtensionTarget = RenderOrderStatusExtensionTarget, ->(): Customer | undefined { +>(): OrderStatusCustomer | undefined { const buyerIdentity = useApi().buyerIdentity; if (!buyerIdentity) { @@ -66,10 +64,6 @@ export function usePhone< return useSubscription(buyerIdentity.phone); } -type PurchasingCompany = Target extends RenderOrderStatusExtensionTarget - ? OrderStatusPurchasingCompany | undefined - : CustomerAccountPurchasingCompany | undefined; - /** * Provides information about the company and its location that the business customer * is purchasing on behalf of during a B2B checkout. It includes details that can be utilized to @@ -78,8 +72,8 @@ type PurchasingCompany = Target extends RenderOrderStatusExtensionTarget * The value is `undefined` if a business customer isn't logged in. This function throws an error if the app doesn't have access to customer data. */ export function usePurchasingCompany< - Target extends RenderExtensionTarget = RenderExtensionTarget, ->(): PurchasingCompany { + Target extends RenderOrderStatusExtensionTarget = RenderOrderStatusExtensionTarget, +>(): OrderStatusPurchasingCompany | undefined { const buyerIdentity = useApi().buyerIdentity; if (!buyerIdentity) { @@ -88,7 +82,5 @@ export function usePurchasingCompany< ); } - return useSubscription( - buyerIdentity.purchasingCompany, - ) as PurchasingCompany; + return useSubscription(buyerIdentity.purchasingCompany); } diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/hooks/index.ts b/packages/ui-extensions-react/src/surfaces/customer-account/hooks/index.ts index 71dd64a23..a4530bd18 100644 --- a/packages/ui-extensions-react/src/surfaces/customer-account/hooks/index.ts +++ b/packages/ui-extensions-react/src/surfaces/customer-account/hooks/index.ts @@ -26,6 +26,10 @@ export { usePhone, usePurchasingCompany, } from './buyer-identity'; +export { + useAuthenticatedAccountCustomer, + useAuthenticatedAccountPurchasingCompany, +} from './authenticated-account'; export {useTranslate} from './translate'; export {useSessionToken} from './session-token'; export {useSettings} from './settings'; diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/hooks/tests/authenticated-account.test.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/hooks/tests/authenticated-account.test.tsx new file mode 100644 index 000000000..3c242f929 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/hooks/tests/authenticated-account.test.tsx @@ -0,0 +1,66 @@ +import {StatefulRemoteSubscribable} from '@remote-ui/async-subscription'; +import {faker} from '@faker-js/faker'; +import { + useAuthenticatedAccountCustomer, + useAuthenticatedAccountPurchasingCompany, +} from '../authenticated-account'; + +import {mount, createMockStatefulRemoteSubscribable} from './mount'; + +function createMockCustomer() { + return { + id: `gid://shopify/Customer/${faker.datatype.number({ + min: 1, + precision: 1, + })}`, + }; +} + +function createMockPurchasingCompany() { + return { + company: { + id: `gid://shopify/Company/${faker.datatype.number({ + min: 1, + precision: 1, + })}`, + }, + }; +} + +function createMockHookContext(customer = {}, purchasingCompany = {}) { + return { + extensionApi: { + authenticatedAccount: { + customer: createMockStatefulRemoteSubscribable(customer), + purchasingCompany: + createMockStatefulRemoteSubscribable(purchasingCompany), + }, + }, + }; +} + +describe('account Hooks', () => { + describe('useLoggedInAccountCustomer()', () => { + it('returns id of the logged in customer', () => { + const customer = createMockCustomer(); + const {value} = mount.hook( + () => useAuthenticatedAccountCustomer(), + createMockHookContext(customer), + ); + expect(customer).toBeDefined(); + expect(customer.id).toBeDefined(); + expect(value.id).toBe(customer.id); + }); + + it('returns company id of which the logged in customer belongs to', () => { + const purchasingCompany = createMockPurchasingCompany(); + const {value} = mount.hook( + () => useAuthenticatedAccountPurchasingCompany(), + createMockHookContext({}, purchasingCompany), + ); + expect(purchasingCompany).toBeDefined(); + expect(purchasingCompany.company.id).toBeDefined(); + expect(value.company.id).toBe(purchasingCompany.company.id); + }); + }); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/api.ts b/packages/ui-extensions/src/surfaces/customer-account/api.ts index c15d66b0f..0f98761ea 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/api.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/api.ts @@ -2,7 +2,7 @@ export type { CartCost, CartLineCost, CheckoutSettings, - Customer, + OrderStatusCustomer, ExtensionSettings, Merchandise, ImageDetails, @@ -63,6 +63,10 @@ export type { I18n, Language, Storage, + AuthenticatedAccount, + PurchasingCompany, + Company, + Customer, } from './api/shared'; export type {CartLineItemApi} from './api/cart-line/cart-line-item'; @@ -77,9 +81,6 @@ export type { NavigationOptions, NavigationType, NavigateFunction, - BuyerIdentity, - PurchasingCompany, - Company, Localization, CompanyLocationApi, OrderApi, diff --git a/packages/ui-extensions/src/surfaces/customer-account/api/order-status/order-status.ts b/packages/ui-extensions/src/surfaces/customer-account/api/order-status/order-status.ts index 83d8cbb03..41942fb8f 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/api/order-status/order-status.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/api/order-status/order-status.ts @@ -12,6 +12,7 @@ import type { I18n, Language, Storage, + AuthenticatedAccount, } from '../shared'; import type {ExtensionTarget} from '../../targets'; import {Extension} from '../shared'; @@ -388,6 +389,11 @@ export interface OrderStatusApi { * @example 'unstable' */ version: Version; + + /** + * Information about the authenticated account. + */ + authenticatedAccount: AuthenticatedAccount; } export interface Ui { @@ -413,7 +419,7 @@ export interface OrderStatusBuyerIdentity { * * {% include /apps/checkout/privacy-icon.md %} Requires access to [protected customer data](/docs/apps/store/data-protection/protected-customer-data). */ - customer: StatefulRemoteSubscribable; + customer: StatefulRemoteSubscribable; /** * The email address of the buyer that is interacting with the cart. @@ -878,7 +884,7 @@ export interface CartCustomDiscountAllocation * * {% include /apps/checkout/privacy-icon.md %} Requires access to [protected customer data](/docs/apps/store/data-protection/protected-customer-data). */ -export interface Customer { +export interface OrderStatusCustomer { /** * Customer ID. * diff --git a/packages/ui-extensions/src/surfaces/customer-account/api/shared.ts b/packages/ui-extensions/src/surfaces/customer-account/api/shared.ts index 343743c3a..8f726bbc6 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/api/shared.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/api/shared.ts @@ -1162,3 +1162,43 @@ export interface MailingAddress { */ phone?: string; } + +export interface AuthenticatedAccount { + /** + * Provides the company info of the authenticated business customer. + * If the customer is not authenticated or is not a business customer, this value is `undefined`. + */ + purchasingCompany: StatefulRemoteSubscribable; + /** + * Provides the customer information of the authenticated customer. + */ + customer: StatefulRemoteSubscribable; +} + +/** + * Information about the authenticated customer. + * + * {% include /apps/checkout/privacy-icon.md %} Requires access to [protected customer data](/docs/apps/store/data-protection/protected-customer-data). + */ +export interface Customer { + /** + * Customer ID. + * + * @example 'gid://shopify/Customer/123' + */ + id: string; +} + +export interface PurchasingCompany { + /** + * Include information of the company of the logged in business customer. + */ + company: Company; +} + +export interface Company { + /** + * Company ID. + */ + id: string; +} diff --git a/packages/ui-extensions/src/surfaces/customer-account/api/standard-api/standard-api.ts b/packages/ui-extensions/src/surfaces/customer-account/api/standard-api/standard-api.ts index 7587afe61..dc682126d 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/api/standard-api/standard-api.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/api/standard-api/standard-api.ts @@ -1,4 +1,10 @@ -import {Extension, I18n, Storage, Language} from '../shared'; +import { + Extension, + I18n, + Storage, + Language, + AuthenticatedAccount, +} from '../shared'; import type {ExtensionTarget} from '../../targets'; import {StatefulRemoteSubscribable} from '@remote-ui/async-subscription'; @@ -26,9 +32,9 @@ export interface StandardApi { extension: Extension; /** - * Information about the buyer. + * Information about the authenticated account. */ - buyerIdentity: BuyerIdentity; + authenticatedAccount: AuthenticatedAccount; /** * The renderer version being used for the extension. @@ -101,28 +107,6 @@ export interface OrderApi { orderId: string; } -export interface BuyerIdentity { - /** - * Provides the company info that the business customer is purchasing on behalf of. - * If the buyer is not a business customer, this value is `undefined`. - */ - purchasingCompany: StatefulRemoteSubscribable; -} - -export interface PurchasingCompany { - /** - * Include information of the company that the business customer is purchasing on behalf of. - */ - company: Company; -} - -export interface Company { - /** - * Company ID. - */ - id: string; -} - export interface Localization { /** * The language the buyer sees in the customer account hub.