mirror of
https://github.com/vercel/commerce.git
synced 2025-05-18 07:26:59 +00:00
prettier
This commit is contained in:
parent
02b477bdea
commit
3afc174b15
@ -1,33 +1,31 @@
|
|||||||
import { normalizeSearchProducts } from '../../../utils/normalise-product';
|
import { normalizeSearchProducts } from '../../../utils/normalise-product'
|
||||||
import { ProductsEndpoint } from '.'
|
import { ProductsEndpoint } from '.'
|
||||||
|
|
||||||
|
|
||||||
const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
|
const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
|
||||||
req,
|
req,
|
||||||
res,
|
res,
|
||||||
body: { search, categoryId, brandId, sort },
|
body: { search, categoryId, brandId, sort },
|
||||||
config
|
config,
|
||||||
}) => {
|
}) => {
|
||||||
const { sdk } = config;
|
const { sdk } = config
|
||||||
|
|
||||||
// 'clothing' is our main category default, and a manually set category has priority
|
// 'clothing' is our main category default, and a manually set category has priority
|
||||||
const searchTerm = categoryId ? categoryId as string : search || 'clothing';
|
const searchTerm = categoryId ? (categoryId as string) : search || 'clothing'
|
||||||
|
|
||||||
const searchClient = await sdk.getSearchClient();
|
const searchClient = await sdk.getSearchClient()
|
||||||
// use SDK search API for initial products
|
// use SDK search API for initial products
|
||||||
const searchResults = await searchClient.productSearch({
|
const searchResults = await searchClient.productSearch({
|
||||||
parameters: {
|
parameters: {
|
||||||
q: searchTerm,
|
q: searchTerm,
|
||||||
limit: 20
|
limit: 20,
|
||||||
}
|
},
|
||||||
});
|
})
|
||||||
let products = [];
|
let products = []
|
||||||
let found = false;
|
let found = false
|
||||||
if (searchResults.total) {
|
if (searchResults.total) {
|
||||||
found = true;
|
found = true
|
||||||
products = normalizeSearchProducts(searchResults.hits) as any[];
|
products = normalizeSearchProducts(searchResults.hits) as any[]
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).json({ data: { products, found } })
|
res.status(200).json({ data: { products, found } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import getAllProductPaths from './operations/get-all-product-paths'
|
|||||||
import getAllProducts from './operations/get-all-products'
|
import getAllProducts from './operations/get-all-products'
|
||||||
import getProduct from './operations/get-product'
|
import getProduct from './operations/get-product'
|
||||||
|
|
||||||
|
|
||||||
export interface SFCCConfig extends CommerceAPIConfig {
|
export interface SFCCConfig extends CommerceAPIConfig {
|
||||||
sdk: Sdk
|
sdk: Sdk
|
||||||
}
|
}
|
||||||
@ -22,8 +21,7 @@ const config: SFCCConfig = {
|
|||||||
customerCookie: '',
|
customerCookie: '',
|
||||||
cartCookieMaxAge: 2592000,
|
cartCookieMaxAge: 2592000,
|
||||||
fetch: createFetcher(() => getCommerceApi().getConfig()),
|
fetch: createFetcher(() => getCommerceApi().getConfig()),
|
||||||
sdk // SalesForce Cloud Commerce API SDK
|
sdk, // SalesForce Cloud Commerce API SDK
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const operations = {
|
const operations = {
|
||||||
@ -39,7 +37,9 @@ const operations = {
|
|||||||
export const provider = { config, operations }
|
export const provider = { config, operations }
|
||||||
|
|
||||||
export type Provider = typeof provider
|
export type Provider = typeof provider
|
||||||
export type SFCCProviderAPI<P extends Provider = Provider> = CommerceAPI<P | any>
|
export type SFCCProviderAPI<P extends Provider = Provider> = CommerceAPI<
|
||||||
|
P | any
|
||||||
|
>
|
||||||
|
|
||||||
export function getCommerceApi<P extends Provider>(
|
export function getCommerceApi<P extends Provider>(
|
||||||
customProvider: P = provider as any
|
customProvider: P = provider as any
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import { Product } from '@vercel/commerce/types/product'
|
import { Product } from '@vercel/commerce/types/product'
|
||||||
import { OperationContext } from '@vercel/commerce/api/operations'
|
import { OperationContext } from '@vercel/commerce/api/operations'
|
||||||
import { normalizeSearchProducts } from '../utils/normalise-product';
|
import { normalizeSearchProducts } from '../utils/normalise-product'
|
||||||
import { SFCCConfig } from '..'
|
import { SFCCConfig } from '..'
|
||||||
|
|
||||||
export type GetAllProductPathsResult = {
|
export type GetAllProductPathsResult = {
|
||||||
products: Array<{ path: string }>
|
products: Array<{ path: string }>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function getAllProductPathsOperation({ commerce }: OperationContext<any>) {
|
export default function getAllProductPathsOperation({
|
||||||
|
commerce,
|
||||||
|
}: OperationContext<any>) {
|
||||||
async function getAllProductPaths({
|
async function getAllProductPaths({
|
||||||
query,
|
query,
|
||||||
config,
|
config,
|
||||||
@ -17,23 +19,22 @@ export default function getAllProductPathsOperation({ commerce }: OperationConte
|
|||||||
config?: SFCCConfig
|
config?: SFCCConfig
|
||||||
variables?: any
|
variables?: any
|
||||||
} = {}): Promise<GetAllProductPathsResult> {
|
} = {}): Promise<GetAllProductPathsResult> {
|
||||||
|
|
||||||
// TODO: support locale
|
// TODO: support locale
|
||||||
const { sdk, locale } = commerce.getConfig(config) as SFCCConfig
|
const { sdk, locale } = commerce.getConfig(config) as SFCCConfig
|
||||||
const searchClient = await sdk.getSearchClient()
|
const searchClient = await sdk.getSearchClient()
|
||||||
|
|
||||||
// use SDK search API for initial products same as getAllProductsOperation
|
// use SDK search API for initial products same as getAllProductsOperation
|
||||||
const searchResults = await searchClient.productSearch({
|
const searchResults = await searchClient.productSearch({
|
||||||
parameters: { q: "dress", limit: variables?.first },
|
parameters: { q: 'dress', limit: variables?.first },
|
||||||
});
|
})
|
||||||
|
|
||||||
let products = [] as Product[];
|
let products = [] as Product[]
|
||||||
|
|
||||||
if (searchResults.total) {
|
if (searchResults.total) {
|
||||||
products = normalizeSearchProducts(searchResults.hits)
|
products = normalizeSearchProducts(searchResults.hits)
|
||||||
} else {
|
} else {
|
||||||
// TODO: handle this better?
|
// TODO: handle this better?
|
||||||
console.log("No results for search");
|
console.log('No results for search')
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -2,7 +2,7 @@ import { Product } from '@vercel/commerce/types/product'
|
|||||||
import { GetAllProductsOperation } from '@vercel/commerce/types/product'
|
import { GetAllProductsOperation } from '@vercel/commerce/types/product'
|
||||||
import type { OperationContext } from '@vercel/commerce/api/operations'
|
import type { OperationContext } from '@vercel/commerce/api/operations'
|
||||||
import type { SFCCConfig } from '../index'
|
import type { SFCCConfig } from '../index'
|
||||||
import { normalizeSearchProducts } from '../utils/normalise-product';
|
import { normalizeSearchProducts } from '../utils/normalise-product'
|
||||||
|
|
||||||
export default function getAllProductsOperation({
|
export default function getAllProductsOperation({
|
||||||
commerce,
|
commerce,
|
||||||
@ -17,26 +17,25 @@ export default function getAllProductsOperation({
|
|||||||
config?: Partial<SFCCConfig>
|
config?: Partial<SFCCConfig>
|
||||||
preview?: boolean
|
preview?: boolean
|
||||||
} = {}): Promise<{ products: Product[] | any[] }> {
|
} = {}): Promise<{ products: Product[] | any[] }> {
|
||||||
|
|
||||||
// TODO: support locale
|
// TODO: support locale
|
||||||
const { sdk, locale } = commerce.getConfig(config) as SFCCConfig
|
const { sdk, locale } = commerce.getConfig(config) as SFCCConfig
|
||||||
const searchClient = await sdk.getSearchClient()
|
const searchClient = await sdk.getSearchClient()
|
||||||
|
|
||||||
// use SDK search API for initial products
|
// use SDK search API for initial products
|
||||||
const searchResults = await searchClient.productSearch({
|
const searchResults = await searchClient.productSearch({
|
||||||
parameters: { q: "dress", limit: variables?.first },
|
parameters: { q: 'dress', limit: variables?.first },
|
||||||
});
|
})
|
||||||
|
|
||||||
let products = [] as Product[];
|
let products = [] as Product[]
|
||||||
|
|
||||||
if (searchResults.total) {
|
if (searchResults.total) {
|
||||||
products = normalizeSearchProducts(searchResults.hits)
|
products = normalizeSearchProducts(searchResults.hits)
|
||||||
} else {
|
} else {
|
||||||
// TODO: handle this better?
|
// TODO: handle this better?
|
||||||
console.log("No results for search");
|
console.log('No results for search')
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
products: products
|
products: products,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getAllProducts
|
return getAllProducts
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
|
|
||||||
import { GetProductOperation, Product } from '@vercel/commerce/types/product'
|
import { GetProductOperation, Product } from '@vercel/commerce/types/product'
|
||||||
import type { SFCCConfig } from '../index'
|
import type { SFCCConfig } from '../index'
|
||||||
import type { OperationContext } from '@vercel/commerce/api/operations'
|
import type { OperationContext } from '@vercel/commerce/api/operations'
|
||||||
import { normalizeProduct } from '../utils/normalise-product';
|
import { normalizeProduct } from '../utils/normalise-product'
|
||||||
|
|
||||||
export default function getProductOperation({
|
export default function getProductOperation({
|
||||||
commerce,
|
commerce,
|
||||||
@ -17,11 +16,12 @@ export default function getProductOperation({
|
|||||||
config?: Partial<SFCCConfig>
|
config?: Partial<SFCCConfig>
|
||||||
preview?: boolean
|
preview?: boolean
|
||||||
} = {}): Promise<Product | {} | any> {
|
} = {}): Promise<Product | {} | any> {
|
||||||
|
|
||||||
// TODO: support locale
|
// TODO: support locale
|
||||||
const { sdk, locale } = commerce.getConfig(config) as SFCCConfig
|
const { sdk, locale } = commerce.getConfig(config) as SFCCConfig
|
||||||
const shopperProductsClient = await sdk.getshopperProductsClient()
|
const shopperProductsClient = await sdk.getshopperProductsClient()
|
||||||
const product = await shopperProductsClient.getProduct({parameters: {id: variables?.slug as string}});
|
const product = await shopperProductsClient.getProduct({
|
||||||
|
parameters: { id: variables?.slug as string },
|
||||||
|
})
|
||||||
const normalizedProduct = normalizeProduct(product)
|
const normalizedProduct = normalizeProduct(product)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { ClientConfig, Customer } from "commerce-sdk";
|
import { ClientConfig, Customer } from 'commerce-sdk'
|
||||||
|
|
||||||
// client configuration parameters
|
// client configuration parameters
|
||||||
export const clientConfig: ClientConfig = {
|
export const clientConfig: ClientConfig = {
|
||||||
headers: {
|
headers: {
|
||||||
authorization: ``
|
authorization: ``,
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
clientId: process.env.SFCC_CLIENT_ID || '',
|
clientId: process.env.SFCC_CLIENT_ID || '',
|
||||||
@ -11,7 +11,7 @@ export const clientConfig: ClientConfig = {
|
|||||||
shortCode: process.env.SFCC_SHORT_CODE || '',
|
shortCode: process.env.SFCC_SHORT_CODE || '',
|
||||||
siteId: process.env.SFCC_SITE_ID || '',
|
siteId: process.env.SFCC_SITE_ID || '',
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the shopper or guest JWT/access token, along with a refresh token, using client credentials
|
* Get the shopper or guest JWT/access token, along with a refresh token, using client credentials
|
||||||
@ -19,25 +19,24 @@ export const clientConfig: ClientConfig = {
|
|||||||
* @returns guest user authorization token
|
* @returns guest user authorization token
|
||||||
*/
|
*/
|
||||||
export async function getGuestUserAuthToken(): Promise<Customer.ShopperLogin.TokenResponse> {
|
export async function getGuestUserAuthToken(): Promise<Customer.ShopperLogin.TokenResponse> {
|
||||||
const credentials = `${process.env.SFCC_CLIENT_ID}:${process.env.SFCC_CLIENT_SECRET}`;
|
const credentials = `${process.env.SFCC_CLIENT_ID}:${process.env.SFCC_CLIENT_SECRET}`
|
||||||
const base64data = Buffer.from(credentials).toString("base64");
|
const base64data = Buffer.from(credentials).toString('base64')
|
||||||
const headers = { Authorization: `Basic ${base64data}` };
|
const headers = { Authorization: `Basic ${base64data}` }
|
||||||
const client = new Customer.ShopperLogin(clientConfig);
|
const client = new Customer.ShopperLogin(clientConfig)
|
||||||
|
|
||||||
return await client.getAccessToken({
|
return await client.getAccessToken({
|
||||||
headers,
|
headers,
|
||||||
body: {
|
body: {
|
||||||
grant_type: "client_credentials",
|
grant_type: 'client_credentials',
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getConfigAuth = async () => {
|
export const getConfigAuth = async () => {
|
||||||
const shopperToken = await getGuestUserAuthToken();
|
const shopperToken = await getGuestUserAuthToken()
|
||||||
const configAuth = {
|
const configAuth = {
|
||||||
...clientConfig,
|
...clientConfig,
|
||||||
headers: {"authorization":`Bearer ${shopperToken.access_token}`}
|
headers: { authorization: `Bearer ${shopperToken.access_token}` },
|
||||||
};
|
}
|
||||||
return configAuth;
|
return configAuth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,46 +1,58 @@
|
|||||||
import { Product as SFCCProduct, Search } from "commerce-sdk";
|
import { Product as SFCCProduct, Search } from 'commerce-sdk'
|
||||||
import type { Product, ProductImage, ProductOption, ProductVariant } from '@vercel/commerce/types/product'
|
import type {
|
||||||
|
Product,
|
||||||
|
ProductImage,
|
||||||
|
ProductOption,
|
||||||
|
ProductVariant,
|
||||||
|
} from '@vercel/commerce/types/product'
|
||||||
|
|
||||||
const normaliseOptions = (options: SFCCProduct.ShopperProducts.Product["variationAttributes"]): Product["options"] => {
|
const normaliseOptions = (
|
||||||
|
options: SFCCProduct.ShopperProducts.Product['variationAttributes']
|
||||||
|
): Product['options'] => {
|
||||||
if (!Array.isArray(options)) return []
|
if (!Array.isArray(options)) return []
|
||||||
|
|
||||||
return options.map(option => {
|
return options.map((option) => {
|
||||||
return {
|
return {
|
||||||
id: option.id,
|
id: option.id,
|
||||||
displayName: option.name as string,
|
displayName: option.name as string,
|
||||||
values: option.values!.map(value => ({label: value.name}))
|
values: option.values!.map((value) => ({ label: value.name })),
|
||||||
} as ProductOption
|
} as ProductOption
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const normaliseVariants = (variants: SFCCProduct.ShopperProducts.Product["variants"]): Product["variants"] => {
|
const normaliseVariants = (
|
||||||
|
variants: SFCCProduct.ShopperProducts.Product['variants']
|
||||||
|
): Product['variants'] => {
|
||||||
if (!Array.isArray(variants)) return []
|
if (!Array.isArray(variants)) return []
|
||||||
|
|
||||||
return variants.map(variant => {
|
return variants.map((variant) => {
|
||||||
|
const options = [] as ProductOption[]
|
||||||
const options = [] as ProductOption[];
|
|
||||||
|
|
||||||
if (variant.variationValues) {
|
if (variant.variationValues) {
|
||||||
for (const [key, value] of Object.entries(variant.variationValues)) {
|
for (const [key, value] of Object.entries(variant.variationValues)) {
|
||||||
const variantOptionObject = {
|
const variantOptionObject = {
|
||||||
id: `${variant.productId}-${key}`,
|
id: `${variant.productId}-${key}`,
|
||||||
displayName: key,
|
displayName: key,
|
||||||
values: [{
|
values: [
|
||||||
|
{
|
||||||
label: value,
|
label: value,
|
||||||
}]
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
options.push(variantOptionObject);
|
options.push(variantOptionObject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: variant.productId,
|
id: variant.productId,
|
||||||
options
|
options,
|
||||||
} as ProductVariant;
|
} as ProductVariant
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeProduct(product: SFCCProduct.ShopperProducts.Product): Product {
|
export function normalizeProduct(
|
||||||
|
product: SFCCProduct.ShopperProducts.Product
|
||||||
|
): Product {
|
||||||
return {
|
return {
|
||||||
id: product.id,
|
id: product.id,
|
||||||
// TODO: use `name-ID` as a virtual slug (for search 1:1)
|
// TODO: use `name-ID` as a virtual slug (for search 1:1)
|
||||||
@ -49,36 +61,36 @@ export function normalizeProduct(product: SFCCProduct.ShopperProducts.Product):
|
|||||||
description: product.longDescription!,
|
description: product.longDescription!,
|
||||||
price: {
|
price: {
|
||||||
value: product.price!,
|
value: product.price!,
|
||||||
currencyCode: product.currency
|
currencyCode: product.currency,
|
||||||
},
|
},
|
||||||
images: product.imageGroups![0].images.map(image => ({
|
images: product.imageGroups![0].images.map((image) => ({
|
||||||
url: image.disBaseLink,
|
url: image.disBaseLink,
|
||||||
altText: image.title
|
altText: image.title,
|
||||||
})) as ProductImage[],
|
})) as ProductImage[],
|
||||||
variants: normaliseVariants(product.variants),
|
variants: normaliseVariants(product.variants),
|
||||||
options: normaliseOptions(product.variationAttributes),
|
options: normaliseOptions(product.variationAttributes),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeSearchProducts(products: Search.ShopperSearch.ProductSearchHit[]): Product[] {
|
export function normalizeSearchProducts(
|
||||||
|
products: Search.ShopperSearch.ProductSearchHit[]
|
||||||
return products.map(product => ({
|
): Product[] {
|
||||||
|
return products.map((product) => ({
|
||||||
id: product.productId,
|
id: product.productId,
|
||||||
slug: product.productId, // use product ID as a slug
|
slug: product.productId, // use product ID as a slug
|
||||||
name: product.productName!,
|
name: product.productName!,
|
||||||
description: '',
|
description: '',
|
||||||
price: {
|
price: {
|
||||||
value: product.price!,
|
value: product.price!,
|
||||||
currencyCode: product.currency
|
currencyCode: product.currency,
|
||||||
},
|
},
|
||||||
images: [
|
images: [
|
||||||
{
|
{
|
||||||
url: product.image!.link,
|
url: product.image!.link,
|
||||||
altText: product.productName
|
altText: product.productName,
|
||||||
} as ProductImage
|
} as ProductImage,
|
||||||
],
|
],
|
||||||
variants: normaliseVariants(product.variants),
|
variants: normaliseVariants(product.variants),
|
||||||
options: normaliseOptions(product.variationAttributes),
|
options: normaliseOptions(product.variationAttributes),
|
||||||
}));
|
}))
|
||||||
|
|
||||||
}
|
}
|
@ -1,19 +1,19 @@
|
|||||||
import { Product, Search } from "commerce-sdk";
|
import { Product, Search } from 'commerce-sdk'
|
||||||
import { getConfigAuth } from "./get-auth-token";
|
import { getConfigAuth } from './get-auth-token'
|
||||||
|
|
||||||
const getSearchClient = async () => {
|
const getSearchClient = async () => {
|
||||||
const configAuth = await getConfigAuth();
|
const configAuth = await getConfigAuth()
|
||||||
return new Search.ShopperSearch(configAuth);
|
return new Search.ShopperSearch(configAuth)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getshopperProductsClient = async () => {
|
const getshopperProductsClient = async () => {
|
||||||
const configAuth = await getConfigAuth();
|
const configAuth = await getConfigAuth()
|
||||||
return new Product.ShopperProducts(configAuth)
|
return new Product.ShopperProducts(configAuth)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sdk = {
|
export const sdk = {
|
||||||
getshopperProductsClient,
|
getshopperProductsClient,
|
||||||
getSearchClient
|
getSearchClient,
|
||||||
}
|
}
|
||||||
export type Sdk = typeof sdk
|
export type Sdk = typeof sdk
|
||||||
export default sdk
|
export default sdk
|
Loading…
x
Reference in New Issue
Block a user