Skip to content

Commit

Permalink
Merge pull request #1441 from Shopify/Add-account-to-standard-API
Browse files Browse the repository at this point in the history
Add authenticated account to standard api
  • Loading branch information
brianshen1990 authored Oct 18, 2023
2 parents a45b482 + f365a8f commit f669e74
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -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<Target>().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<Target>().authenticatedAccount;

return useSubscription(account.purchasingCompany);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import type {
Customer,
OrderStatusCustomer,
OrderStatusPurchasingCompany,
PurchasingCompany as CustomerAccountPurchasingCompany,
RenderExtensionTarget,
RenderOrderStatusExtensionTarget,
} from '@shopify/ui-extensions/customer-account';

Expand All @@ -18,7 +16,7 @@ import {useSubscription} from './subscription';
*/
export function useCustomer<
Target extends RenderOrderStatusExtensionTarget = RenderOrderStatusExtensionTarget,
>(): Customer | undefined {
>(): OrderStatusCustomer | undefined {
const buyerIdentity = useApi<Target>().buyerIdentity;

if (!buyerIdentity) {
Expand Down Expand Up @@ -66,10 +64,6 @@ export function usePhone<
return useSubscription(buyerIdentity.phone);
}

type PurchasingCompany<Target> = 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
Expand All @@ -78,8 +72,8 @@ type PurchasingCompany<Target> = 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> {
Target extends RenderOrderStatusExtensionTarget = RenderOrderStatusExtensionTarget,
>(): OrderStatusPurchasingCompany | undefined {
const buyerIdentity = useApi<Target>().buyerIdentity;

if (!buyerIdentity) {
Expand All @@ -88,7 +82,5 @@ export function usePurchasingCompany<
);
}

return useSubscription(
buyerIdentity.purchasingCompany,
) as PurchasingCompany<Target>;
return useSubscription(buyerIdentity.purchasingCompany);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
});
});
});
9 changes: 5 additions & 4 deletions packages/ui-extensions/src/surfaces/customer-account/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export type {
CartCost,
CartLineCost,
CheckoutSettings,
Customer,
OrderStatusCustomer,
ExtensionSettings,
Merchandise,
ImageDetails,
Expand Down Expand Up @@ -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';
Expand All @@ -77,9 +81,6 @@ export type {
NavigationOptions,
NavigationType,
NavigateFunction,
BuyerIdentity,
PurchasingCompany,
Company,
Localization,
CompanyLocationApi,
OrderApi,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
I18n,
Language,
Storage,
AuthenticatedAccount,
} from '../shared';
import type {ExtensionTarget} from '../../targets';
import {Extension} from '../shared';
Expand Down Expand Up @@ -388,6 +389,11 @@ export interface OrderStatusApi<Target extends ExtensionTarget> {
* @example 'unstable'
*/
version: Version;

/**
* Information about the authenticated account.
*/
authenticatedAccount: AuthenticatedAccount;
}

export interface Ui {
Expand All @@ -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 | undefined>;
customer: StatefulRemoteSubscribable<OrderStatusCustomer | undefined>;

/**
* The email address of the buyer that is interacting with the cart.
Expand Down Expand Up @@ -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.
*
Expand Down
40 changes: 40 additions & 0 deletions packages/ui-extensions/src/surfaces/customer-account/api/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<PurchasingCompany | undefined>;
/**
* Provides the customer information of the authenticated customer.
*/
customer: StatefulRemoteSubscribable<Customer | undefined>;
}

/**
* 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;
}
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -26,9 +32,9 @@ export interface StandardApi<Target extends ExtensionTarget = ExtensionTarget> {
extension: Extension;

/**
* Information about the buyer.
* Information about the authenticated account.
*/
buyerIdentity: BuyerIdentity;
authenticatedAccount: AuthenticatedAccount;

/**
* The renderer version being used for the extension.
Expand Down Expand Up @@ -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<PurchasingCompany | undefined>;
}

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.
Expand Down

0 comments on commit f669e74

Please sign in to comment.