diff --git a/components/wishlist/index.ts b/components/wishlist/index.ts
index 470e6682c..8aee9f816 100644
--- a/components/wishlist/index.ts
+++ b/components/wishlist/index.ts
@@ -1 +1,2 @@
export { default as WishlistCard } from './WishlistCard'
+export { default as WishlistButton } from './WishlistButton'
diff --git a/config/seo.json b/config/seo.json
index 0d0c3d37d..82520cf9b 100644
--- a/config/seo.json
+++ b/config/seo.json
@@ -1,12 +1,22 @@
{
"title": "ACME Storefront | Powered by Next.js Commerce",
"titleTemplate": "%s - ACME Storefront",
- "description": "Next.js Commerce -> https://www.nextjs.org/commerce",
+ "description": "Next.js Commerce - https://www.nextjs.org/commerce",
"openGraph": {
+ "title": "ACME Storefront | Powered by Next.js Commerce",
+ "description": "Next.js Commerce - https://www.nextjs.org/commerce",
"type": "website",
"locale": "en_IE",
"url": "https://nextjs.org/commerce",
- "site_name": "Next.js Commerce"
+ "site_name": "Next.js Commerce",
+ "images": [
+ {
+ "url": "/card.png",
+ "width": 800,
+ "height": 600,
+ "alt": "Next.js Commerce"
+ }
+ ]
},
"twitter": {
"handle": "@nextjs",
diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md
deleted file mode 100644
index 437766dd8..000000000
--- a/docs/ROADMAP.md
+++ /dev/null
@@ -1 +0,0 @@
-# Roadmap
diff --git a/framework/bigcommerce/.env.template b/framework/bigcommerce/.env.template
new file mode 100644
index 000000000..2b91bc095
--- /dev/null
+++ b/framework/bigcommerce/.env.template
@@ -0,0 +1,8 @@
+COMMERCE_PROVIDER=bigcommerce
+
+BIGCOMMERCE_STOREFRONT_API_URL=
+BIGCOMMERCE_STOREFRONT_API_TOKEN=
+BIGCOMMERCE_STORE_API_URL=
+BIGCOMMERCE_STORE_API_TOKEN=
+BIGCOMMERCE_STORE_API_CLIENT_ID=
+BIGCOMMERCE_CHANNEL_ID=
diff --git a/framework/bigcommerce/README.md b/framework/bigcommerce/README.md
index 7beee6c14..7f62a5f3f 100644
--- a/framework/bigcommerce/README.md
+++ b/framework/bigcommerce/README.md
@@ -1,47 +1,34 @@
+# Bigcommerce Provider
-Table of Contents
-=================
+**Demo:** https://bigcommerce.demo.vercel.store/
- * [BigCommerce Storefront Data Hooks](#bigcommerce-storefront-data-hooks)
- * [Installation](#installation)
- * [General Usage](#general-usage)
- * [CommerceProvider](#commerceprovider)
- * [useLogin hook](#uselogin-hook)
- * [useLogout](#uselogout)
- * [useCustomer](#usecustomer)
- * [useSignup](#usesignup)
- * [usePrice](#useprice)
- * [Cart Hooks](#cart-hooks)
- * [useCart](#usecart)
- * [useAddItem](#useadditem)
- * [useUpdateItem](#useupdateitem)
- * [useRemoveItem](#useremoveitem)
- * [Wishlist Hooks](#wishlist-hooks)
- * [Product Hooks and API](#product-hooks-and-api)
- * [useSearch](#usesearch)
- * [getAllProducts](#getallproducts)
- * [getProduct](#getproduct)
- * [More](#more)
+With the deploy button below you'll be able to have a [BigCommerce](https://www.bigcommerce.com/) account and a store that works with this starter:
-# BigCommerce Storefront Data Hooks
+[](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fcommerce&project-name=commerce&repo-name=commerce&demo-title=Next.js%20Commerce&demo-description=An%20all-in-one%20starter%20kit%20for%20high-performance%20e-commerce%20sites.&demo-url=https%3A%2F%2Fdemo.vercel.store&demo-image=https%3A%2F%2Fbigcommerce-demo-asset-ksvtgfvnd.vercel.app%2Fbigcommerce.png&integration-ids=oac_MuWZiE4jtmQ2ejZQaQ7ncuDT)
-> This project is under active development, new features and updates will be continuously added over time
+If you already have a BigCommerce account and want to use your current store, then copy the `.env.template` file in this directory to `.env.local` in the main directory (which will be ignored by Git):
-UI hooks and data fetching methods built from the ground up for e-commerce applications written in React, that use BigCommerce as a headless e-commerce platform. The package provides:
-
-- Code splitted hooks for data fetching using [SWR](https://swr.vercel.app/), and to handle common user actions
-- Code splitted data fetching methods for initial data population and static generation of content
-- Helpers to create the API endpoints that connect to the hooks, very well suited for Next.js applications
-
-## Installation
-
-To install:
-
-```
-yarn add storefront-data-hooks
+```bash
+cp framework/bigcommerce/.env.template .env.local
```
-After install, the first thing you do is:
set your environment variables in `.env.local`
+Then, set the environment variables in `.env.local` to match the ones from your store.
+
+## Contribute
+
+Our commitment to Open Source can be found [here](https://vercel.com/oss).
+
+If you find an issue with the provider or want a new feature, feel free to open a PR or [create a new issue](https://github.com/vercel/commerce/issues).
+
+## Troubleshoot
+
+
+I already own a BigCommerce store. What should I do?
+
+First thing you do is: set your environment variables
+
+
+.env.local
```sh
BIGCOMMERCE_STOREFRONT_API_URL=<>
@@ -49,335 +36,24 @@ BIGCOMMERCE_STOREFRONT_API_TOKEN=<>
BIGCOMMERCE_STORE_API_URL=<>
BIGCOMMERCE_STORE_API_TOKEN=<>
BIGCOMMERCE_STORE_API_CLIENT_ID=<>
+BIGCOMMERCE_CHANNEL_ID=<>
```
-## General Usage
+If your project was started with a "Deploy with Vercel" button, you can use Vercel's CLI to retrieve these credentials.
-### CommerceProvider
+1. Install Vercel CLI: `npm i -g vercel`
+2. Link local instance with Vercel and Github accounts (creates .vercel file): `vercel link`
+3. Download your environment variables: `vercel env pull .env.local`
-This component is a provider pattern component that creates commerce context for it's children. It takes config values for the locale and an optional `fetcherRef` object for data fetching.
+Next, you're free to customize the starter. More updates coming soon. Stay tuned.
-```jsx
-...
-import { CommerceProvider } from '@bigcommerce/storefront-data-hooks'
+
-const App = ({ locale = 'en-US', children }) => {
- return (
-
- {children}
-
- )
-}
-...
-```
-
-### useLogin hook
-
-Hook for bigcommerce user login functionality, returns `login` function to handle user login.
-
-```jsx
-...
-import useLogin from '@bigcommerce/storefront-data-hooks/use-login'
-
-const LoginView = () => {
- const login = useLogin()
-
- const handleLogin = async () => {
- await login({
- email,
- password,
- })
- }
-
- return (
-
- )
-}
-...
-```
-
-### useLogout
-
-Hook to logout user.
-
-```jsx
-...
-import useLogout from '@bigcommerce/storefront-data-hooks/use-logout'
-
-const LogoutLink = () => {
- const logout = useLogout()
- return (
-
logout()}>
- Logout
-
- )
-}
-```
-
-### useCustomer
-
-Hook for getting logged in customer data, and fetching customer info.
-
-```jsx
-...
-import useCustomer from '@bigcommerce/storefront-data-hooks/use-customer'
-...
-
-const Profile = () => {
- const { data } = useCustomer()
-
- if (!data) {
- return null
- }
-
- return (
-
Hello, {data.firstName}
- )
-}
-```
-
-### useSignup
-
-Hook for bigcommerce user signup, returns `signup` function to handle user signups.
-
-```jsx
-...
-import useSignup from '@bigcommerce/storefront-data-hooks/use-login'
-
-const SignupView = () => {
- const signup = useSignup()
-
- const handleSignup = async () => {
- await signup({
- email,
- firstName,
- lastName,
- password,
- })
- }
-
- return (
-
- )
-}
-...
-```
-
-### usePrice
-
-Helper hook to format price according to commerce locale, and return discount if available.
-
-```jsx
-import usePrice from '@bigcommerce/storefront-data-hooks/use-price'
-...
- const { price, discount, basePrice } = usePrice(
- data && {
- amount: data.cart_amount,
- currencyCode: data.currency.code,
- }
- )
-...
-```
-
-## Cart Hooks
-
-### useCart
-
-Returns the current cart data for use
-
-```jsx
-...
-import useCart from '@bigcommerce/storefront-data-hooks/cart/use-cart'
-
-const countItem = (count: number, item: any) => count + item.quantity
-const countItems = (count: number, items: any[]) =>
- items.reduce(countItem, count)
-
-const CartNumber = () => {
- const { data } = useCart()
- const itemsCount = Object.values(data?.line_items ?? {}).reduce(countItems, 0)
-
- return itemsCount > 0 ?
{itemsCount} : null
-}
-```
-
-### useAddItem
-
-```jsx
-...
-import useAddItem from '@bigcommerce/storefront-data-hooks/cart/use-add-item'
-
-const AddToCartButton = ({ productId, variantId }) => {
- const addItem = useAddItem()
-
- const addToCart = async () => {
- await addItem({
- productId,
- variantId,
- })
- }
-
- return
Add To Cart
-}
-...
-```
-
-### useUpdateItem
-
-```jsx
-...
-import useUpdateItem from '@bigcommerce/storefront-data-hooks/cart/use-update-item'
-
-const CartItem = ({ item }) => {
- const [quantity, setQuantity] = useState(item.quantity)
- const updateItem = useUpdateItem(item)
-
- const updateQuantity = async (e) => {
- const val = e.target.value
- await updateItem({ quantity: val })
- }
-
- return (
-
- )
-}
-...
-```
-
-### useRemoveItem
-
-Provided with a cartItemId, will remove an item from the cart:
-
-```jsx
-...
-import useRemoveItem from '@bigcommerce/storefront-data-hooks/cart/use-remove-item'
-
-const RemoveButton = ({ item }) => {
- const removeItem = useRemoveItem()
-
- const handleRemove = async () => {
- await removeItem({ id: item.id })
- }
-
- return
Remove
-}
-...
-```
-
-## Wishlist Hooks
-
-Wishlist hooks are similar to cart hooks. See the below example for how to use `useWishlist`, `useAddItem`, and `useRemoveItem`.
-
-```jsx
-import useAddItem from '@bigcommerce/storefront-data-hooks/wishlist/use-add-item'
-import useRemoveItem from '@bigcommerce/storefront-data-hooks/wishlist/use-remove-item'
-import useWishlist from '@bigcommerce/storefront-data-hooks/wishlist/use-wishlist'
-
-const WishlistButton = ({ productId, variant }) => {
- const addItem = useAddItem()
- const removeItem = useRemoveItem()
- const { data } = useWishlist()
- const { data: customer } = useCustomer()
- const itemInWishlist = data?.items?.find(
- (item) =>
- item.product_id === productId &&
- item.variant_id === variant?.node.entityId
- )
-
- const handleWishlistChange = async (e) => {
- e.preventDefault()
-
- if (!customer) {
- return
- }
-
- if (itemInWishlist) {
- await removeItem({ id: itemInWishlist.id! })
- } else {
- await addItem({
- productId,
- variantId: variant?.node.entityId!,
- })
- }
- }
-
- return (
-
-
-
- )
-}
-```
-
-## Product Hooks and API
-
-### useSearch
-
-`useSearch` handles searching the bigcommerce storefront product catalog by catalog, brand, and query string.
-
-```jsx
-...
-import useSearch from '@bigcommerce/storefront-data-hooks/products/use-search'
-
-const SearchPage = ({ searchString, category, brand, sortStr }) => {
- const { data } = useSearch({
- search: searchString || '',
- categoryId: category?.entityId,
- brandId: brand?.entityId,
- sort: sortStr || '',
- })
-
- return (
-
- {data.products.map(({ node }) => (
-
- ))}
-
- )
-}
-```
-
-### getAllProducts
-
-API function to retrieve a product list.
-
-```js
-import { getConfig } from '@bigcommerce/storefront-data-hooks/api'
-import getAllProducts from '@bigcommerce/storefront-data-hooks/api/operations/get-all-products'
-
-const { products } = await getAllProducts({
- variables: { field: 'featuredProducts', first: 6 },
- config,
- preview,
-})
-```
-
-### getProduct
-
-API product to retrieve a single product when provided with the product
-slug string.
-
-```js
-import { getConfig } from '@bigcommerce/storefront-data-hooks/api'
-import getProduct from '@bigcommerce/storefront-data-hooks/api/operations/get-product'
-
-const { product } = await getProduct({
- variables: { slug },
- config,
- preview,
-})
-```
-
-## More
-
-Feel free to read through the source for more usage, and check the commerce vercel demo and commerce repo for usage examples: ([demo.vercel.store](https://demo.vercel.store/)) ([repo](https://github.com/vercel/commerce))
+
+BigCommerce shows a Coming Soon page and requests a Preview Code
+
+After Email confirmation, Checkout should be manually enabled through BigCommerce platform. Look for "Review & test your store" section through BigCommerce's dashboard.
+
+
+BigCommerce team has been notified and they plan to add more detailed about this subject.
+
diff --git a/framework/bigcommerce/api/cart/index.ts b/framework/bigcommerce/api/cart/index.ts
deleted file mode 100644
index 5ff2d975b..000000000
--- a/framework/bigcommerce/api/cart/index.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-import isAllowedMethod from '../utils/is-allowed-method'
-import createApiHandler, {
- BigcommerceApiHandler,
- BigcommerceHandler,
-} from '../utils/create-api-handler'
-import { BigcommerceApiError } from '../utils/errors'
-import getCart from './handlers/get-cart'
-import addItem from './handlers/add-item'
-import updateItem from './handlers/update-item'
-import removeItem from './handlers/remove-item'
-
-type OptionSelections = {
- option_id: Number
- option_value: Number|String
-}
-
-export type ItemBody = {
- productId: number
- variantId: number
- quantity?: number
- optionSelections?: OptionSelections
-}
-
-export type AddItemBody = { item: ItemBody }
-
-export type UpdateItemBody = { itemId: string; item: ItemBody }
-
-export type RemoveItemBody = { itemId: string }
-
-// TODO: this type should match:
-// https://developer.bigcommerce.com/api-reference/cart-checkout/server-server-cart-api/cart/getacart#responses
-export type Cart = {
- id: string
- parent_id?: string
- customer_id: number
- email: string
- currency: { code: string }
- tax_included: boolean
- base_amount: number
- discount_amount: number
- cart_amount: number
- line_items: {
- custom_items: any[]
- digital_items: any[]
- gift_certificates: any[]
- physical_items: any[]
- }
- // TODO: add missing fields
-}
-
-export type CartHandlers = {
- getCart: BigcommerceHandler
- addItem: BigcommerceHandler>
- updateItem: BigcommerceHandler<
- Cart,
- { cartId?: string } & Partial
- >
- removeItem: BigcommerceHandler<
- Cart,
- { cartId?: string } & Partial
- >
-}
-
-const METHODS = ['GET', 'POST', 'PUT', 'DELETE']
-
-// TODO: a complete implementation should have schema validation for `req.body`
-const cartApi: BigcommerceApiHandler = async (
- req,
- res,
- config,
- handlers
-) => {
- if (!isAllowedMethod(req, res, METHODS)) return
-
- const { cookies } = req
- const cartId = cookies[config.cartCookie]
-
- try {
- // Return current cart info
- if (req.method === 'GET') {
- const body = { cartId }
- return await handlers['getCart']({ req, res, config, body })
- }
-
- // Create or add an item to the cart
- if (req.method === 'POST') {
- const body = { ...req.body, cartId }
- return await handlers['addItem']({ req, res, config, body })
- }
-
- // Update item in cart
- if (req.method === 'PUT') {
- const body = { ...req.body, cartId }
- return await handlers['updateItem']({ req, res, config, body })
- }
-
- // Remove an item from the cart
- if (req.method === 'DELETE') {
- const body = { ...req.body, cartId }
- return await handlers['removeItem']({ req, res, config, body })
- }
- } catch (error) {
- console.error(error)
-
- const message =
- error instanceof BigcommerceApiError
- ? 'An unexpected error ocurred with the Bigcommerce API'
- : 'An unexpected error ocurred'
-
- res.status(500).json({ data: null, errors: [{ message }] })
- }
-}
-
-export const handlers = { getCart, addItem, updateItem, removeItem }
-
-export default createApiHandler(cartApi, handlers, {})
diff --git a/framework/bigcommerce/api/catalog/products.ts b/framework/bigcommerce/api/catalog/products.ts
deleted file mode 100644
index 0e3690e55..000000000
--- a/framework/bigcommerce/api/catalog/products.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import isAllowedMethod from '../utils/is-allowed-method'
-import createApiHandler, {
- BigcommerceApiHandler,
- BigcommerceHandler,
-} from '../utils/create-api-handler'
-import { BigcommerceApiError } from '../utils/errors'
-import type { ProductEdge } from '../operations/get-all-products'
-import getProducts from './handlers/get-products'
-
-export type SearchProductsData = {
- products: ProductEdge[]
- found: boolean
-}
-
-export type ProductsHandlers = {
- getProducts: BigcommerceHandler<
- SearchProductsData,
- { search?: 'string'; category?: string; brand?: string; sort?: string }
- >
-}
-
-const METHODS = ['GET']
-
-// TODO: a complete implementation should have schema validation for `req.body`
-const productsApi: BigcommerceApiHandler<
- SearchProductsData,
- ProductsHandlers
-> = async (req, res, config, handlers) => {
- if (!isAllowedMethod(req, res, METHODS)) return
-
- try {
- const body = req.query
- return await handlers['getProducts']({ req, res, config, body })
- } catch (error) {
- console.error(error)
-
- const message =
- error instanceof BigcommerceApiError
- ? 'An unexpected error ocurred with the Bigcommerce API'
- : 'An unexpected error ocurred'
-
- res.status(500).json({ data: null, errors: [{ message }] })
- }
-}
-
-export const handlers = { getProducts }
-
-export default createApiHandler(productsApi, handlers, {})
diff --git a/framework/bigcommerce/api/checkout.ts b/framework/bigcommerce/api/checkout.ts
deleted file mode 100644
index 530f4c40a..000000000
--- a/framework/bigcommerce/api/checkout.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-import isAllowedMethod from './utils/is-allowed-method'
-import createApiHandler, {
- BigcommerceApiHandler,
-} from './utils/create-api-handler'
-import { BigcommerceApiError } from './utils/errors'
-
-const METHODS = ['GET']
-const fullCheckout = true
-
-// TODO: a complete implementation should have schema validation for `req.body`
-const checkoutApi: BigcommerceApiHandler = async (req, res, config) => {
- if (!isAllowedMethod(req, res, METHODS)) return
-
- const { cookies } = req
- const cartId = cookies[config.cartCookie]
-
- try {
- if (!cartId) {
- res.redirect('/cart')
- return
- }
-
- const { data } = await config.storeApiFetch(
- `/v3/carts/${cartId}/redirect_urls`,
- {
- method: 'POST',
- }
- )
-
- if (fullCheckout) {
- res.redirect(data.checkout_url)
- return
- }
-
- // TODO: make the embedded checkout work too!
- const html = `
-
-
-
-
-
- Checkout
-
-
-
-
-
-
-
- `
-
- res.status(200)
- res.setHeader('Content-Type', 'text/html')
- res.write(html)
- res.end()
- } catch (error) {
- console.error(error)
-
- const message =
- error instanceof BigcommerceApiError
- ? 'An unexpected error ocurred with the Bigcommerce API'
- : 'An unexpected error ocurred'
-
- res.status(500).json({ data: null, errors: [{ message }] })
- }
-}
-
-export default createApiHandler(checkoutApi, {}, {})
diff --git a/framework/bigcommerce/api/customers/index.ts b/framework/bigcommerce/api/customers/index.ts
deleted file mode 100644
index 5af4d1d1d..000000000
--- a/framework/bigcommerce/api/customers/index.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import createApiHandler, {
- BigcommerceApiHandler,
- BigcommerceHandler,
-} from '../utils/create-api-handler'
-import isAllowedMethod from '../utils/is-allowed-method'
-import { BigcommerceApiError } from '../utils/errors'
-import getLoggedInCustomer, {
- Customer,
-} from './handlers/get-logged-in-customer'
-
-export type { Customer }
-
-export type CustomerData = {
- customer: Customer
-}
-
-export type CustomersHandlers = {
- getLoggedInCustomer: BigcommerceHandler
-}
-
-const METHODS = ['GET']
-
-const customersApi: BigcommerceApiHandler<
- CustomerData,
- CustomersHandlers
-> = async (req, res, config, handlers) => {
- if (!isAllowedMethod(req, res, METHODS)) return
-
- try {
- const body = null
- return await handlers['getLoggedInCustomer']({ req, res, config, body })
- } catch (error) {
- console.error(error)
-
- const message =
- error instanceof BigcommerceApiError
- ? 'An unexpected error ocurred with the Bigcommerce API'
- : 'An unexpected error ocurred'
-
- res.status(500).json({ data: null, errors: [{ message }] })
- }
-}
-
-const handlers = { getLoggedInCustomer }
-
-export default createApiHandler(customersApi, handlers, {})
diff --git a/framework/bigcommerce/api/customers/login.ts b/framework/bigcommerce/api/customers/login.ts
deleted file mode 100644
index e8f24a92d..000000000
--- a/framework/bigcommerce/api/customers/login.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import createApiHandler, {
- BigcommerceApiHandler,
- BigcommerceHandler,
-} from '../utils/create-api-handler'
-import isAllowedMethod from '../utils/is-allowed-method'
-import { BigcommerceApiError } from '../utils/errors'
-import login from './handlers/login'
-
-export type LoginBody = {
- email: string
- password: string
-}
-
-export type LoginHandlers = {
- login: BigcommerceHandler>
-}
-
-const METHODS = ['POST']
-
-const loginApi: BigcommerceApiHandler = async (
- req,
- res,
- config,
- handlers
-) => {
- if (!isAllowedMethod(req, res, METHODS)) return
-
- try {
- const body = req.body ?? {}
- return await handlers['login']({ req, res, config, body })
- } catch (error) {
- console.error(error)
-
- const message =
- error instanceof BigcommerceApiError
- ? 'An unexpected error ocurred with the Bigcommerce API'
- : 'An unexpected error ocurred'
-
- res.status(500).json({ data: null, errors: [{ message }] })
- }
-}
-
-const handlers = { login }
-
-export default createApiHandler(loginApi, handlers, {})
diff --git a/framework/bigcommerce/api/customers/logout.ts b/framework/bigcommerce/api/customers/logout.ts
deleted file mode 100644
index 0a4965245..000000000
--- a/framework/bigcommerce/api/customers/logout.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import createApiHandler, {
- BigcommerceApiHandler,
- BigcommerceHandler,
-} from '../utils/create-api-handler'
-import isAllowedMethod from '../utils/is-allowed-method'
-import { BigcommerceApiError } from '../utils/errors'
-import logout from './handlers/logout'
-
-export type LogoutHandlers = {
- logout: BigcommerceHandler
-}
-
-const METHODS = ['GET']
-
-const logoutApi: BigcommerceApiHandler = async (
- req,
- res,
- config,
- handlers
-) => {
- if (!isAllowedMethod(req, res, METHODS)) return
-
- try {
- const redirectTo = req.query.redirect_to
- const body = typeof redirectTo === 'string' ? { redirectTo } : {}
-
- return await handlers['logout']({ req, res, config, body })
- } catch (error) {
- console.error(error)
-
- const message =
- error instanceof BigcommerceApiError
- ? 'An unexpected error ocurred with the Bigcommerce API'
- : 'An unexpected error ocurred'
-
- res.status(500).json({ data: null, errors: [{ message }] })
- }
-}
-
-const handlers = { logout }
-
-export default createApiHandler(logoutApi, handlers, {})
diff --git a/framework/bigcommerce/api/customers/signup.ts b/framework/bigcommerce/api/customers/signup.ts
deleted file mode 100644
index aa26f78cf..000000000
--- a/framework/bigcommerce/api/customers/signup.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import createApiHandler, {
- BigcommerceApiHandler,
- BigcommerceHandler,
-} from '../utils/create-api-handler'
-import isAllowedMethod from '../utils/is-allowed-method'
-import { BigcommerceApiError } from '../utils/errors'
-import signup from './handlers/signup'
-
-export type SignupBody = {
- firstName: string
- lastName: string
- email: string
- password: string
-}
-
-export type SignupHandlers = {
- signup: BigcommerceHandler>
-}
-
-const METHODS = ['POST']
-
-const signupApi: BigcommerceApiHandler = async (
- req,
- res,
- config,
- handlers
-) => {
- if (!isAllowedMethod(req, res, METHODS)) return
-
- const { cookies } = req
- const cartId = cookies[config.cartCookie]
-
- try {
- const body = { ...req.body, cartId }
- return await handlers['signup']({ req, res, config, body })
- } catch (error) {
- console.error(error)
-
- const message =
- error instanceof BigcommerceApiError
- ? 'An unexpected error ocurred with the Bigcommerce API'
- : 'An unexpected error ocurred'
-
- res.status(500).json({ data: null, errors: [{ message }] })
- }
-}
-
-const handlers = { signup }
-
-export default createApiHandler(signupApi, handlers, {})
diff --git a/framework/bigcommerce/api/cart/handlers/add-item.ts b/framework/bigcommerce/api/endpoints/cart/add-item.ts
similarity index 61%
rename from framework/bigcommerce/api/cart/handlers/add-item.ts
rename to framework/bigcommerce/api/endpoints/cart/add-item.ts
index 25ae3880e..52ef1223d 100644
--- a/framework/bigcommerce/api/cart/handlers/add-item.ts
+++ b/framework/bigcommerce/api/endpoints/cart/add-item.ts
@@ -1,9 +1,9 @@
+import { normalizeCart } from '../../../lib/normalize'
import { parseCartItem } from '../../utils/parse-item'
import getCartCookie from '../../utils/get-cart-cookie'
-import type { CartHandlers } from '..'
+import type { CartEndpoint } from '.'
-// Return current cart info
-const addItem: CartHandlers['addItem'] = async ({
+const addItem: CartEndpoint['handlers']['addItem'] = async ({
res,
body: { cartId, item },
config,
@@ -26,15 +26,21 @@ const addItem: CartHandlers['addItem'] = async ({
}),
}
const { data } = cartId
- ? await config.storeApiFetch(`/v3/carts/${cartId}/items`, options)
- : await config.storeApiFetch('/v3/carts', options)
+ ? await config.storeApiFetch(
+ `/v3/carts/${cartId}/items?include=line_items.physical_items.options`,
+ options
+ )
+ : await config.storeApiFetch(
+ '/v3/carts?include=line_items.physical_items.options',
+ options
+ )
// Create or update the cart cookie
res.setHeader(
'Set-Cookie',
getCartCookie(config.cartCookie, data.id, config.cartCookieMaxAge)
)
- res.status(200).json({ data })
+ res.status(200).json({ data: normalizeCart(data) })
}
export default addItem
diff --git a/framework/bigcommerce/api/cart/handlers/get-cart.ts b/framework/bigcommerce/api/endpoints/cart/get-cart.ts
similarity index 52%
rename from framework/bigcommerce/api/cart/handlers/get-cart.ts
rename to framework/bigcommerce/api/endpoints/cart/get-cart.ts
index 9fb42d730..d3bb309e2 100644
--- a/framework/bigcommerce/api/cart/handlers/get-cart.ts
+++ b/framework/bigcommerce/api/endpoints/cart/get-cart.ts
@@ -1,18 +1,22 @@
+import { normalizeCart } from '../../../lib/normalize'
import { BigcommerceApiError } from '../../utils/errors'
import getCartCookie from '../../utils/get-cart-cookie'
-import type { Cart, CartHandlers } from '..'
+import type { BigcommerceCart } from '../../../types/cart'
+import type { CartEndpoint } from '.'
// Return current cart info
-const getCart: CartHandlers['getCart'] = async ({
+const getCart: CartEndpoint['handlers']['getCart'] = async ({
res,
body: { cartId },
config,
}) => {
- let result: { data?: Cart } = {}
+ let result: { data?: BigcommerceCart } = {}
if (cartId) {
try {
- result = await config.storeApiFetch(`/v3/carts/${cartId}`)
+ result = await config.storeApiFetch(
+ `/v3/carts/${cartId}?include=line_items.physical_items.options`
+ )
} catch (error) {
if (error instanceof BigcommerceApiError && error.status === 404) {
// Remove the cookie if it exists but the cart wasn't found
@@ -23,7 +27,9 @@ const getCart: CartHandlers['getCart'] = async ({
}
}
- res.status(200).json({ data: result.data ?? null })
+ res.status(200).json({
+ data: result.data ? normalizeCart(result.data) : null,
+ })
}
export default getCart
diff --git a/framework/bigcommerce/api/endpoints/cart/index.ts b/framework/bigcommerce/api/endpoints/cart/index.ts
new file mode 100644
index 000000000..ae2414d70
--- /dev/null
+++ b/framework/bigcommerce/api/endpoints/cart/index.ts
@@ -0,0 +1,26 @@
+import { GetAPISchema, createEndpoint } from '@commerce/api'
+import cartEndpoint from '@commerce/api/endpoints/cart'
+import type { CartSchema } from '../../../types/cart'
+import type { BigcommerceAPI } from '../..'
+import getCart from './get-cart'
+import addItem from './add-item'
+import updateItem from './update-item'
+import removeItem from './remove-item'
+
+export type CartAPI = GetAPISchema
+
+export type CartEndpoint = CartAPI['endpoint']
+
+export const handlers: CartEndpoint['handlers'] = {
+ getCart,
+ addItem,
+ updateItem,
+ removeItem,
+}
+
+const cartApi = createEndpoint({
+ handler: cartEndpoint,
+ handlers,
+})
+
+export default cartApi
diff --git a/framework/bigcommerce/api/cart/handlers/remove-item.ts b/framework/bigcommerce/api/endpoints/cart/remove-item.ts
similarity index 68%
rename from framework/bigcommerce/api/cart/handlers/remove-item.ts
rename to framework/bigcommerce/api/endpoints/cart/remove-item.ts
index 2ee5dbe16..baf10c80f 100644
--- a/framework/bigcommerce/api/cart/handlers/remove-item.ts
+++ b/framework/bigcommerce/api/endpoints/cart/remove-item.ts
@@ -1,8 +1,8 @@
+import { normalizeCart } from '../../../lib/normalize'
import getCartCookie from '../../utils/get-cart-cookie'
-import type { CartHandlers } from '..'
+import type { CartEndpoint } from '.'
-// Return current cart info
-const removeItem: CartHandlers['removeItem'] = async ({
+const removeItem: CartEndpoint['handlers']['removeItem'] = async ({
res,
body: { cartId, itemId },
config,
@@ -15,7 +15,7 @@ const removeItem: CartHandlers['removeItem'] = async ({
}
const result = await config.storeApiFetch<{ data: any } | null>(
- `/v3/carts/${cartId}/items/${itemId}`,
+ `/v3/carts/${cartId}/items/${itemId}?include=line_items.physical_items.options`,
{ method: 'DELETE' }
)
const data = result?.data ?? null
@@ -28,7 +28,7 @@ const removeItem: CartHandlers['removeItem'] = async ({
: // Remove the cart cookie if the cart was removed (empty items)
getCartCookie(config.cartCookie)
)
- res.status(200).json({ data })
+ res.status(200).json({ data: data && normalizeCart(data) })
}
export default removeItem
diff --git a/framework/bigcommerce/api/cart/handlers/update-item.ts b/framework/bigcommerce/api/endpoints/cart/update-item.ts
similarity index 68%
rename from framework/bigcommerce/api/cart/handlers/update-item.ts
rename to framework/bigcommerce/api/endpoints/cart/update-item.ts
index c64c111df..113553fad 100644
--- a/framework/bigcommerce/api/cart/handlers/update-item.ts
+++ b/framework/bigcommerce/api/endpoints/cart/update-item.ts
@@ -1,9 +1,9 @@
+import { normalizeCart } from '../../../lib/normalize'
import { parseCartItem } from '../../utils/parse-item'
import getCartCookie from '../../utils/get-cart-cookie'
-import type { CartHandlers } from '..'
+import type { CartEndpoint } from '.'
-// Return current cart info
-const updateItem: CartHandlers['updateItem'] = async ({
+const updateItem: CartEndpoint['handlers']['updateItem'] = async ({
res,
body: { cartId, itemId, item },
config,
@@ -16,7 +16,7 @@ const updateItem: CartHandlers['updateItem'] = async ({
}
const { data } = await config.storeApiFetch(
- `/v3/carts/${cartId}/items/${itemId}`,
+ `/v3/carts/${cartId}/items/${itemId}?include=line_items.physical_items.options`,
{
method: 'PUT',
body: JSON.stringify({
@@ -30,7 +30,7 @@ const updateItem: CartHandlers['updateItem'] = async ({
'Set-Cookie',
getCartCookie(config.cartCookie, cartId, config.cartCookieMaxAge)
)
- res.status(200).json({ data })
+ res.status(200).json({ data: normalizeCart(data) })
}
export default updateItem
diff --git a/framework/bigcommerce/api/catalog/handlers/get-products.ts b/framework/bigcommerce/api/endpoints/catalog/products/get-products.ts
similarity index 64%
rename from framework/bigcommerce/api/catalog/handlers/get-products.ts
rename to framework/bigcommerce/api/endpoints/catalog/products/get-products.ts
index b05548e40..6dde39e28 100644
--- a/framework/bigcommerce/api/catalog/handlers/get-products.ts
+++ b/framework/bigcommerce/api/endpoints/catalog/products/get-products.ts
@@ -1,18 +1,20 @@
-import getAllProducts, { ProductEdge } from '../../operations/get-all-products'
-import type { ProductsHandlers } from '../products'
+import { Product } from '@commerce/types/product'
+import { ProductsEndpoint } from '.'
const SORT: { [key: string]: string | undefined } = {
latest: 'id',
trending: 'total_sold',
price: 'price',
}
+
const LIMIT = 12
// Return current cart info
-const getProducts: ProductsHandlers['getProducts'] = async ({
+const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
res,
- body: { search, category, brand, sort },
+ body: { search, categoryId, brandId, sort },
config,
+ commerce,
}) => {
// Use a dummy base as we only care about the relative path
const url = new URL('/v3/catalog/products', 'http://a')
@@ -22,11 +24,11 @@ const getProducts: ProductsHandlers['getProducts'] = async ({
if (search) url.searchParams.set('keyword', search)
- if (category && Number.isInteger(Number(category)))
- url.searchParams.set('categories:in', category)
+ if (categoryId && Number.isInteger(Number(categoryId)))
+ url.searchParams.set('categories:in', String(categoryId))
- if (brand && Number.isInteger(Number(brand)))
- url.searchParams.set('brand_id', brand)
+ if (brandId && Number.isInteger(Number(brandId)))
+ url.searchParams.set('brand_id', String(brandId))
if (sort) {
const [_sort, direction] = sort.split('-')
@@ -44,25 +46,29 @@ const getProducts: ProductsHandlers['getProducts'] = async ({
const { data } = await config.storeApiFetch<{ data: { id: number }[] }>(
url.pathname + url.search
)
- const entityIds = data.map((p) => p.id)
- const found = entityIds.length > 0
+
+ const ids = data.map((p) => String(p.id))
+ const found = ids.length > 0
+
// We want the GraphQL version of each product
- const graphqlData = await getAllProducts({
- variables: { first: LIMIT, entityIds },
+ const graphqlData = await commerce.getAllProducts({
+ variables: { first: LIMIT, ids },
config,
})
+
// Put the products in an object that we can use to get them by id
const productsById = graphqlData.products.reduce<{
- [k: number]: ProductEdge
+ [k: string]: Product
}>((prods, p) => {
- prods[p.node.entityId] = p
+ prods[Number(p.id)] = p
return prods
}, {})
- const products: ProductEdge[] = found ? [] : graphqlData.products
+
+ const products: Product[] = found ? [] : graphqlData.products
// Populate the products array with the graphql products, in the order
// assigned by the list of entity ids
- entityIds.forEach((id) => {
+ ids.forEach((id) => {
const product = productsById[id]
if (product) products.push(product)
})
diff --git a/framework/bigcommerce/api/endpoints/catalog/products/index.ts b/framework/bigcommerce/api/endpoints/catalog/products/index.ts
new file mode 100644
index 000000000..555740f60
--- /dev/null
+++ b/framework/bigcommerce/api/endpoints/catalog/products/index.ts
@@ -0,0 +1,18 @@
+import { GetAPISchema, createEndpoint } from '@commerce/api'
+import productsEndpoint from '@commerce/api/endpoints/catalog/products'
+import type { ProductsSchema } from '../../../../types/product'
+import type { BigcommerceAPI } from '../../..'
+import getProducts from './get-products'
+
+export type ProductsAPI = GetAPISchema
+
+export type ProductsEndpoint = ProductsAPI['endpoint']
+
+export const handlers: ProductsEndpoint['handlers'] = { getProducts }
+
+const productsApi = createEndpoint({
+ handler: productsEndpoint,
+ handlers,
+})
+
+export default productsApi
diff --git a/framework/bigcommerce/api/endpoints/checkout/checkout.ts b/framework/bigcommerce/api/endpoints/checkout/checkout.ts
new file mode 100644
index 000000000..517a57950
--- /dev/null
+++ b/framework/bigcommerce/api/endpoints/checkout/checkout.ts
@@ -0,0 +1,62 @@
+import type { CheckoutEndpoint } from '.'
+
+const fullCheckout = true
+
+const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({
+ req,
+ res,
+ config,
+}) => {
+ const { cookies } = req
+ const cartId = cookies[config.cartCookie]
+
+ if (!cartId) {
+ res.redirect('/cart')
+ return
+ }
+
+ const { data } = await config.storeApiFetch(
+ `/v3/carts/${cartId}/redirect_urls`,
+ {
+ method: 'POST',
+ }
+ )
+
+ if (fullCheckout) {
+ res.redirect(data.checkout_url)
+ return
+ }
+
+ // TODO: make the embedded checkout work too!
+ const html = `
+
+
+
+
+
+ Checkout
+
+
+
+
+
+
+
+ `
+
+ res.status(200)
+ res.setHeader('Content-Type', 'text/html')
+ res.write(html)
+ res.end()
+}
+
+export default checkout
diff --git a/framework/bigcommerce/api/endpoints/checkout/index.ts b/framework/bigcommerce/api/endpoints/checkout/index.ts
new file mode 100644
index 000000000..eaba32e47
--- /dev/null
+++ b/framework/bigcommerce/api/endpoints/checkout/index.ts
@@ -0,0 +1,18 @@
+import { GetAPISchema, createEndpoint } from '@commerce/api'
+import checkoutEndpoint from '@commerce/api/endpoints/checkout'
+import type { CheckoutSchema } from '../../../types/checkout'
+import type { BigcommerceAPI } from '../..'
+import checkout from './checkout'
+
+export type CheckoutAPI = GetAPISchema
+
+export type CheckoutEndpoint = CheckoutAPI['endpoint']
+
+export const handlers: CheckoutEndpoint['handlers'] = { checkout }
+
+const checkoutApi = createEndpoint({
+ handler: checkoutEndpoint,
+ handlers,
+})
+
+export default checkoutApi
diff --git a/framework/bigcommerce/api/customers/handlers/get-logged-in-customer.ts b/framework/bigcommerce/api/endpoints/customer/get-logged-in-customer.ts
similarity index 89%
rename from framework/bigcommerce/api/customers/handlers/get-logged-in-customer.ts
rename to framework/bigcommerce/api/endpoints/customer/get-logged-in-customer.ts
index 698235dda..cfcce9532 100644
--- a/framework/bigcommerce/api/customers/handlers/get-logged-in-customer.ts
+++ b/framework/bigcommerce/api/endpoints/customer/get-logged-in-customer.ts
@@ -1,5 +1,5 @@
import type { GetLoggedInCustomerQuery } from '../../../schema'
-import type { CustomersHandlers } from '..'
+import type { CustomerEndpoint } from '.'
export const getLoggedInCustomerQuery = /* GraphQL */ `
query getLoggedInCustomer {
@@ -24,7 +24,7 @@ export const getLoggedInCustomerQuery = /* GraphQL */ `
export type Customer = NonNullable
-const getLoggedInCustomer: CustomersHandlers['getLoggedInCustomer'] = async ({
+const getLoggedInCustomer: CustomerEndpoint['handlers']['getLoggedInCustomer'] = async ({
req,
res,
config,
diff --git a/framework/bigcommerce/api/endpoints/customer/index.ts b/framework/bigcommerce/api/endpoints/customer/index.ts
new file mode 100644
index 000000000..cb0f6787a
--- /dev/null
+++ b/framework/bigcommerce/api/endpoints/customer/index.ts
@@ -0,0 +1,18 @@
+import { GetAPISchema, createEndpoint } from '@commerce/api'
+import customerEndpoint from '@commerce/api/endpoints/customer'
+import type { CustomerSchema } from '../../../types/customer'
+import type { BigcommerceAPI } from '../..'
+import getLoggedInCustomer from './get-logged-in-customer'
+
+export type CustomerAPI = GetAPISchema
+
+export type CustomerEndpoint = CustomerAPI['endpoint']
+
+export const handlers: CustomerEndpoint['handlers'] = { getLoggedInCustomer }
+
+const customerApi = createEndpoint({
+ handler: customerEndpoint,
+ handlers,
+})
+
+export default customerApi
diff --git a/framework/bigcommerce/api/endpoints/login/index.ts b/framework/bigcommerce/api/endpoints/login/index.ts
new file mode 100644
index 000000000..2b454c7c2
--- /dev/null
+++ b/framework/bigcommerce/api/endpoints/login/index.ts
@@ -0,0 +1,18 @@
+import { GetAPISchema, createEndpoint } from '@commerce/api'
+import loginEndpoint from '@commerce/api/endpoints/login'
+import type { LoginSchema } from '../../../types/login'
+import type { BigcommerceAPI } from '../..'
+import login from './login'
+
+export type LoginAPI = GetAPISchema
+
+export type LoginEndpoint = LoginAPI['endpoint']
+
+export const handlers: LoginEndpoint['handlers'] = { login }
+
+const loginApi = createEndpoint({
+ handler: loginEndpoint,
+ handlers,
+})
+
+export default loginApi
diff --git a/framework/bigcommerce/api/customers/handlers/login.ts b/framework/bigcommerce/api/endpoints/login/login.ts
similarity index 81%
rename from framework/bigcommerce/api/customers/handlers/login.ts
rename to framework/bigcommerce/api/endpoints/login/login.ts
index c4062f6cc..f55c3b54f 100644
--- a/framework/bigcommerce/api/customers/handlers/login.ts
+++ b/framework/bigcommerce/api/endpoints/login/login.ts
@@ -1,13 +1,13 @@
import { FetcherError } from '@commerce/utils/errors'
-import login from '../../operations/login'
-import type { LoginHandlers } from '../login'
+import type { LoginEndpoint } from '.'
const invalidCredentials = /invalid credentials/i
-const loginHandler: LoginHandlers['login'] = async ({
+const login: LoginEndpoint['handlers']['login'] = async ({
res,
body: { email, password },
config,
+ commerce,
}) => {
// TODO: Add proper validations with something like Ajv
if (!(email && password)) {
@@ -21,7 +21,7 @@ const loginHandler: LoginHandlers['login'] = async ({
// and numeric characters.
try {
- await login({ variables: { email, password }, config, res })
+ await commerce.login({ variables: { email, password }, config, res })
} catch (error) {
// Check if the email and password didn't match an existing account
if (
@@ -46,4 +46,4 @@ const loginHandler: LoginHandlers['login'] = async ({
res.status(200).json({ data: null })
}
-export default loginHandler
+export default login
diff --git a/framework/bigcommerce/api/endpoints/logout/index.ts b/framework/bigcommerce/api/endpoints/logout/index.ts
new file mode 100644
index 000000000..0dbb23bab
--- /dev/null
+++ b/framework/bigcommerce/api/endpoints/logout/index.ts
@@ -0,0 +1,18 @@
+import { GetAPISchema, createEndpoint } from '@commerce/api'
+import logoutEndpoint from '@commerce/api/endpoints/logout'
+import type { LogoutSchema } from '../../../types/logout'
+import type { BigcommerceAPI } from '../..'
+import logout from './logout'
+
+export type LogoutAPI = GetAPISchema
+
+export type LogoutEndpoint = LogoutAPI['endpoint']
+
+export const handlers: LogoutEndpoint['handlers'] = { logout }
+
+const logoutApi = createEndpoint({
+ handler: logoutEndpoint,
+ handlers,
+})
+
+export default logoutApi
diff --git a/framework/bigcommerce/api/customers/handlers/logout.ts b/framework/bigcommerce/api/endpoints/logout/logout.ts
similarity index 74%
rename from framework/bigcommerce/api/customers/handlers/logout.ts
rename to framework/bigcommerce/api/endpoints/logout/logout.ts
index 937ce0954..b90a8c3ce 100644
--- a/framework/bigcommerce/api/customers/handlers/logout.ts
+++ b/framework/bigcommerce/api/endpoints/logout/logout.ts
@@ -1,7 +1,7 @@
import { serialize } from 'cookie'
-import { LogoutHandlers } from '../logout'
+import type { LogoutEndpoint } from '.'
-const logoutHandler: LogoutHandlers['logout'] = async ({
+const logout: LogoutEndpoint['handlers']['logout'] = async ({
res,
body: { redirectTo },
config,
@@ -20,4 +20,4 @@ const logoutHandler: LogoutHandlers['logout'] = async ({
}
}
-export default logoutHandler
+export default logout
diff --git a/framework/bigcommerce/api/endpoints/signup/index.ts b/framework/bigcommerce/api/endpoints/signup/index.ts
new file mode 100644
index 000000000..6ce8be271
--- /dev/null
+++ b/framework/bigcommerce/api/endpoints/signup/index.ts
@@ -0,0 +1,18 @@
+import { GetAPISchema, createEndpoint } from '@commerce/api'
+import signupEndpoint from '@commerce/api/endpoints/signup'
+import type { SignupSchema } from '../../../types/signup'
+import type { BigcommerceAPI } from '../..'
+import signup from './signup'
+
+export type SignupAPI = GetAPISchema
+
+export type SignupEndpoint = SignupAPI['endpoint']
+
+export const handlers: SignupEndpoint['handlers'] = { signup }
+
+const singupApi = createEndpoint({
+ handler: signupEndpoint,
+ handlers,
+})
+
+export default singupApi
diff --git a/framework/bigcommerce/api/customers/handlers/signup.ts b/framework/bigcommerce/api/endpoints/signup/signup.ts
similarity index 87%
rename from framework/bigcommerce/api/customers/handlers/signup.ts
rename to framework/bigcommerce/api/endpoints/signup/signup.ts
index ff45adadb..46c071be8 100644
--- a/framework/bigcommerce/api/customers/handlers/signup.ts
+++ b/framework/bigcommerce/api/endpoints/signup/signup.ts
@@ -1,11 +1,11 @@
import { BigcommerceApiError } from '../../utils/errors'
-import login from '../../operations/login'
-import { SignupHandlers } from '../signup'
+import type { SignupEndpoint } from '.'
-const signup: SignupHandlers['signup'] = async ({
+const signup: SignupEndpoint['handlers']['signup'] = async ({
res,
body: { firstName, lastName, email, password },
config,
+ commerce,
}) => {
// TODO: Add proper validations with something like Ajv
if (!(firstName && lastName && email && password)) {
@@ -54,7 +54,7 @@ const signup: SignupHandlers['signup'] = async ({
}
// Login the customer right after creating it
- await login({ variables: { email, password }, res, config })
+ await commerce.login({ variables: { email, password }, res, config })
res.status(200).json({ data: null })
}
diff --git a/framework/bigcommerce/api/wishlist/handlers/add-item.ts b/framework/bigcommerce/api/endpoints/wishlist/add-item.ts
similarity index 81%
rename from framework/bigcommerce/api/wishlist/handlers/add-item.ts
rename to framework/bigcommerce/api/endpoints/wishlist/add-item.ts
index a02ef4434..4c5970a5d 100644
--- a/framework/bigcommerce/api/wishlist/handlers/add-item.ts
+++ b/framework/bigcommerce/api/endpoints/wishlist/add-item.ts
@@ -1,13 +1,14 @@
-import type { WishlistHandlers } from '..'
-import getCustomerId from '../../operations/get-customer-id'
import getCustomerWishlist from '../../operations/get-customer-wishlist'
import { parseWishlistItem } from '../../utils/parse-item'
+import getCustomerId from './utils/get-customer-id'
+import type { WishlistEndpoint } from '.'
-// Returns the wishlist of the signed customer
-const addItem: WishlistHandlers['addItem'] = async ({
+// Return wishlist info
+const addItem: WishlistEndpoint['handlers']['addItem'] = async ({
res,
body: { customerToken, item },
config,
+ commerce,
}) => {
if (!item) {
return res.status(400).json({
@@ -26,7 +27,7 @@ const addItem: WishlistHandlers['addItem'] = async ({
})
}
- const { wishlist } = await getCustomerWishlist({
+ const { wishlist } = await commerce.getCustomerWishlist({
variables: { customerId },
config,
})
diff --git a/framework/bigcommerce/api/wishlist/handlers/get-wishlist.ts b/framework/bigcommerce/api/endpoints/wishlist/get-wishlist.ts
similarity index 71%
rename from framework/bigcommerce/api/wishlist/handlers/get-wishlist.ts
rename to framework/bigcommerce/api/endpoints/wishlist/get-wishlist.ts
index 3eb3000cc..21443119c 100644
--- a/framework/bigcommerce/api/wishlist/handlers/get-wishlist.ts
+++ b/framework/bigcommerce/api/endpoints/wishlist/get-wishlist.ts
@@ -1,12 +1,14 @@
-import getCustomerId from '../../operations/get-customer-id'
+import type { Wishlist } from '../../../types/wishlist'
+import type { WishlistEndpoint } from '.'
+import getCustomerId from './utils/get-customer-id'
import getCustomerWishlist from '../../operations/get-customer-wishlist'
-import type { Wishlist, WishlistHandlers } from '..'
// Return wishlist info
-const getWishlist: WishlistHandlers['getWishlist'] = async ({
+const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({
res,
body: { customerToken, includeProducts },
config,
+ commerce,
}) => {
let result: { data?: Wishlist } = {}
@@ -22,7 +24,7 @@ const getWishlist: WishlistHandlers['getWishlist'] = async ({
})
}
- const { wishlist } = await getCustomerWishlist({
+ const { wishlist } = await commerce.getCustomerWishlist({
variables: { customerId },
includeProducts,
config,
diff --git a/framework/bigcommerce/api/endpoints/wishlist/index.ts b/framework/bigcommerce/api/endpoints/wishlist/index.ts
new file mode 100644
index 000000000..31af234ce
--- /dev/null
+++ b/framework/bigcommerce/api/endpoints/wishlist/index.ts
@@ -0,0 +1,24 @@
+import { GetAPISchema, createEndpoint } from '@commerce/api'
+import wishlistEndpoint from '@commerce/api/endpoints/wishlist'
+import type { WishlistSchema } from '../../../types/wishlist'
+import type { BigcommerceAPI } from '../..'
+import getWishlist from './get-wishlist'
+import addItem from './add-item'
+import removeItem from './remove-item'
+
+export type WishlistAPI = GetAPISchema
+
+export type WishlistEndpoint = WishlistAPI['endpoint']
+
+export const handlers: WishlistEndpoint['handlers'] = {
+ getWishlist,
+ addItem,
+ removeItem,
+}
+
+const wishlistApi = createEndpoint({
+ handler: wishlistEndpoint,
+ handlers,
+})
+
+export default wishlistApi
diff --git a/framework/bigcommerce/api/wishlist/handlers/remove-item.ts b/framework/bigcommerce/api/endpoints/wishlist/remove-item.ts
similarity index 63%
rename from framework/bigcommerce/api/wishlist/handlers/remove-item.ts
rename to framework/bigcommerce/api/endpoints/wishlist/remove-item.ts
index 29b6eff60..22ac31cf9 100644
--- a/framework/bigcommerce/api/wishlist/handlers/remove-item.ts
+++ b/framework/bigcommerce/api/endpoints/wishlist/remove-item.ts
@@ -1,20 +1,20 @@
-import getCustomerId from '../../operations/get-customer-id'
-import getCustomerWishlist, {
- Wishlist,
-} from '../../operations/get-customer-wishlist'
-import type { WishlistHandlers } from '..'
+import type { Wishlist } from '../../../types/wishlist'
+import getCustomerWishlist from '../../operations/get-customer-wishlist'
+import getCustomerId from './utils/get-customer-id'
+import type { WishlistEndpoint } from '.'
-// Return current wishlist info
-const removeItem: WishlistHandlers['removeItem'] = async ({
+// Return wishlist info
+const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({
res,
body: { customerToken, itemId },
config,
+ commerce,
}) => {
const customerId =
customerToken && (await getCustomerId({ customerToken, config }))
const { wishlist } =
(customerId &&
- (await getCustomerWishlist({
+ (await commerce.getCustomerWishlist({
variables: { customerId },
config,
}))) ||
diff --git a/framework/bigcommerce/api/operations/get-customer-id.ts b/framework/bigcommerce/api/endpoints/wishlist/utils/get-customer-id.ts
similarity index 65%
rename from framework/bigcommerce/api/operations/get-customer-id.ts
rename to framework/bigcommerce/api/endpoints/wishlist/utils/get-customer-id.ts
index 7fe84093e..603f8be2d 100644
--- a/framework/bigcommerce/api/operations/get-customer-id.ts
+++ b/framework/bigcommerce/api/endpoints/wishlist/utils/get-customer-id.ts
@@ -1,5 +1,5 @@
-import { GetCustomerIdQuery } from '../../schema'
-import { BigcommerceConfig, getConfig } from '..'
+import type { GetCustomerIdQuery } from '../../../../schema'
+import type { BigcommerceConfig } from '../../..'
export const getCustomerIdQuery = /* GraphQL */ `
query getCustomerId {
@@ -14,10 +14,8 @@ async function getCustomerId({
config,
}: {
customerToken: string
- config?: BigcommerceConfig
-}): Promise {
- config = getConfig(config)
-
+ config: BigcommerceConfig
+}): Promise {
const { data } = await config.fetch(
getCustomerIdQuery,
undefined,
@@ -28,7 +26,7 @@ async function getCustomerId({
}
)
- return data?.customer?.entityId
+ return String(data?.customer?.entityId)
}
export default getCustomerId
diff --git a/framework/bigcommerce/api/index.ts b/framework/bigcommerce/api/index.ts
index 0216fe61b..f28f2a1f4 100644
--- a/framework/bigcommerce/api/index.ts
+++ b/framework/bigcommerce/api/index.ts
@@ -1,7 +1,28 @@
import type { RequestInit } from '@vercel/fetch'
-import type { CommerceAPIConfig } from '@commerce/api'
-import fetchGraphqlApi from './utils/fetch-graphql-api'
-import fetchStoreApi from './utils/fetch-store-api'
+import {
+ CommerceAPI,
+ CommerceAPIConfig,
+ getCommerceApi as commerceApi,
+} from '@commerce/api'
+import createFetchGraphqlApi from './utils/fetch-graphql-api'
+import createFetchStoreApi from './utils/fetch-store-api'
+
+import type { CartAPI } from './endpoints/cart'
+import type { CustomerAPI } from './endpoints/customer'
+import type { LoginAPI } from './endpoints/login'
+import type { LogoutAPI } from './endpoints/logout'
+import type { SignupAPI } from './endpoints/signup'
+import type { ProductsAPI } from './endpoints/catalog/products'
+import type { WishlistAPI } from './endpoints/wishlist'
+
+import login from './operations/login'
+import getAllPages from './operations/get-all-pages'
+import getPage from './operations/get-page'
+import getSiteInfo from './operations/get-site-info'
+import getCustomerWishlist from './operations/get-customer-wishlist'
+import getAllProductPaths from './operations/get-all-product-paths'
+import getAllProducts from './operations/get-all-products'
+import getProduct from './operations/get-product'
export interface BigcommerceConfig extends CommerceAPIConfig {
// Indicates if the returned metadata with translations should be applied to the
@@ -39,50 +60,52 @@ if (!(STORE_API_URL && STORE_API_TOKEN && STORE_API_CLIENT_ID)) {
)
}
-export class Config {
- private config: BigcommerceConfig
-
- constructor(config: Omit) {
- this.config = {
- ...config,
- // The customerCookie is not customizable for now, BC sets the cookie and it's
- // not important to rename it
- customerCookie: 'SHOP_TOKEN',
- }
- }
-
- getConfig(userConfig: Partial = {}) {
- return Object.entries(userConfig).reduce(
- (cfg, [key, value]) => Object.assign(cfg, { [key]: value }),
- { ...this.config }
- )
- }
-
- setConfig(newConfig: Partial) {
- Object.assign(this.config, newConfig)
- }
-}
-
const ONE_DAY = 60 * 60 * 24
-const config = new Config({
+
+const config: BigcommerceConfig = {
commerceUrl: API_URL,
apiToken: API_TOKEN,
+ customerCookie: 'SHOP_TOKEN',
cartCookie: process.env.BIGCOMMERCE_CART_COOKIE ?? 'bc_cartId',
cartCookieMaxAge: ONE_DAY * 30,
- fetch: fetchGraphqlApi,
+ fetch: createFetchGraphqlApi(() => getCommerceApi().getConfig()),
applyLocale: true,
// REST API only
storeApiUrl: STORE_API_URL,
storeApiToken: STORE_API_TOKEN,
storeApiClientId: STORE_API_CLIENT_ID,
storeChannelId: STORE_CHANNEL_ID,
- storeApiFetch: fetchStoreApi,
-})
-
-export function getConfig(userConfig?: Partial) {
- return config.getConfig(userConfig)
+ storeApiFetch: createFetchStoreApi(() => getCommerceApi().getConfig()),
}
-export function setConfig(newConfig: Partial) {
- return config.setConfig(newConfig)
+const operations = {
+ login,
+ getAllPages,
+ getPage,
+ getSiteInfo,
+ getCustomerWishlist,
+ getAllProductPaths,
+ getAllProducts,
+ getProduct,
+}
+
+export const provider = { config, operations }
+
+export type Provider = typeof provider
+
+export type APIs =
+ | CartAPI
+ | CustomerAPI
+ | LoginAPI
+ | LogoutAPI
+ | SignupAPI
+ | ProductsAPI
+ | WishlistAPI
+
+export type BigcommerceAPI = CommerceAPI
+
+export function getCommerceApi
(
+ customProvider: P = provider as any
+): BigcommerceAPI
{
+ return commerceApi(customProvider)
}
diff --git a/framework/bigcommerce/api/operations/get-all-pages.ts b/framework/bigcommerce/api/operations/get-all-pages.ts
index 21c70c1dc..3a9b64b1f 100644
--- a/framework/bigcommerce/api/operations/get-all-pages.ts
+++ b/framework/bigcommerce/api/operations/get-all-pages.ts
@@ -1,45 +1,46 @@
+import type {
+ OperationContext,
+ OperationOptions,
+} from '@commerce/api/operations'
+import type { Page, GetAllPagesOperation } from '../../types/page'
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
-import { BigcommerceConfig, getConfig } from '..'
-import { definitions } from '../definitions/store-content'
+import { BigcommerceConfig, Provider } from '..'
-export type Page = definitions['page_Full']
+export default function getAllPagesOperation({
+ commerce,
+}: OperationContext) {
+ async function getAllPages(opts?: {
+ config?: Partial
+ preview?: boolean
+ }): Promise
-export type GetAllPagesResult<
- T extends { pages: any[] } = { pages: Page[] }
-> = T
+ async function getAllPages(
+ opts: {
+ config?: Partial
+ preview?: boolean
+ } & OperationOptions
+ ): Promise
-async function getAllPages(opts?: {
- config?: BigcommerceConfig
- preview?: boolean
-}): Promise
+ async function getAllPages({
+ config,
+ preview,
+ }: {
+ url?: string
+ config?: Partial
+ preview?: boolean
+ } = {}): Promise {
+ const cfg = commerce.getConfig(config)
+ // RecursivePartial forces the method to check for every prop in the data, which is
+ // required in case there's a custom `url`
+ const { data } = await cfg.storeApiFetch<
+ RecursivePartial<{ data: Page[] }>
+ >('/v3/content/pages')
+ const pages = (data as RecursiveRequired) ?? []
-async function getAllPages(opts: {
- url: string
- config?: BigcommerceConfig
- preview?: boolean
-}): Promise>
-
-async function getAllPages({
- config,
- preview,
-}: {
- url?: string
- config?: BigcommerceConfig
- preview?: boolean
-} = {}): Promise {
- config = getConfig(config)
- // RecursivePartial forces the method to check for every prop in the data, which is
- // required in case there's a custom `url`
- const { data } = await config.storeApiFetch<
- RecursivePartial<{ data: Page[] }>
- >('/v3/content/pages')
- const pages = (data as RecursiveRequired) ?? []
-
- const retPages = {
- pages: preview ? pages : pages.filter((p) => p.is_visible),
+ return {
+ pages: preview ? pages : pages.filter((p) => p.is_visible),
+ }
}
- return retPages
+ return getAllPages
}
-
-export default getAllPages
diff --git a/framework/bigcommerce/api/operations/get-all-product-paths.ts b/framework/bigcommerce/api/operations/get-all-product-paths.ts
index 71522be35..da7b457eb 100644
--- a/framework/bigcommerce/api/operations/get-all-product-paths.ts
+++ b/framework/bigcommerce/api/operations/get-all-product-paths.ts
@@ -1,10 +1,12 @@
import type {
- GetAllProductPathsQuery,
- GetAllProductPathsQueryVariables,
-} from '../../schema'
+ OperationContext,
+ OperationOptions,
+} from '@commerce/api/operations'
+import type { GetAllProductPathsQuery } from '../../schema'
+import type { GetAllProductPathsOperation } from '../../types/product'
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
import filterEdges from '../utils/filter-edges'
-import { BigcommerceConfig, getConfig } from '..'
+import { BigcommerceConfig, Provider } from '..'
export const getAllProductPathsQuery = /* GraphQL */ `
query getAllProductPaths($first: Int = 100) {
@@ -20,52 +22,45 @@ export const getAllProductPathsQuery = /* GraphQL */ `
}
`
-export type ProductPath = NonNullable<
- NonNullable[0]
->
+export default function getAllProductPathsOperation({
+ commerce,
+}: OperationContext) {
+ async function getAllProductPaths<
+ T extends GetAllProductPathsOperation
+ >(opts?: {
+ variables?: T['variables']
+ config?: BigcommerceConfig
+ }): Promise
-export type ProductPaths = ProductPath[]
+ async function getAllProductPaths(
+ opts: {
+ variables?: T['variables']
+ config?: BigcommerceConfig
+ } & OperationOptions
+ ): Promise
-export type { GetAllProductPathsQueryVariables }
+ async function getAllProductPaths({
+ query = getAllProductPathsQuery,
+ variables,
+ config,
+ }: {
+ query?: string
+ variables?: T['variables']
+ config?: BigcommerceConfig
+ } = {}): Promise {
+ config = commerce.getConfig(config)
+ // RecursivePartial forces the method to check for every prop in the data, which is
+ // required in case there's a custom `query`
+ const { data } = await config.fetch<
+ RecursivePartial
+ >(query, { variables })
+ const products = data.site?.products?.edges
-export type GetAllProductPathsResult<
- T extends { products: any[] } = { products: ProductPaths }
-> = T
-
-async function getAllProductPaths(opts?: {
- variables?: GetAllProductPathsQueryVariables
- config?: BigcommerceConfig
-}): Promise
-
-async function getAllProductPaths<
- T extends { products: any[] },
- V = any
->(opts: {
- query: string
- variables?: V
- config?: BigcommerceConfig
-}): Promise>
-
-async function getAllProductPaths({
- query = getAllProductPathsQuery,
- variables,
- config,
-}: {
- query?: string
- variables?: GetAllProductPathsQueryVariables
- config?: BigcommerceConfig
-} = {}): Promise {
- config = getConfig(config)
- // RecursivePartial forces the method to check for every prop in the data, which is
- // required in case there's a custom `query`
- const { data } = await config.fetch<
- RecursivePartial
- >(query, { variables })
- const products = data.site?.products?.edges
-
- return {
- products: filterEdges(products as RecursiveRequired),
+ return {
+ products: filterEdges(products as RecursiveRequired).map(
+ ({ node }) => node
+ ),
+ }
}
+ return getAllProductPaths
}
-
-export default getAllProductPaths
diff --git a/framework/bigcommerce/api/operations/get-all-products.ts b/framework/bigcommerce/api/operations/get-all-products.ts
index 0cd9737c2..c2652f5bf 100644
--- a/framework/bigcommerce/api/operations/get-all-products.ts
+++ b/framework/bigcommerce/api/operations/get-all-products.ts
@@ -1,12 +1,18 @@
+import type {
+ OperationContext,
+ OperationOptions,
+} from '@commerce/api/operations'
import type {
GetAllProductsQuery,
GetAllProductsQueryVariables,
} from '../../schema'
+import type { GetAllProductsOperation } from '../../types/product'
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
import filterEdges from '../utils/filter-edges'
import setProductLocaleMeta from '../utils/set-product-locale-meta'
import { productConnectionFragment } from '../fragments/product'
-import { BigcommerceConfig, getConfig } from '..'
+import { BigcommerceConfig, Provider } from '..'
+import { normalizeProduct } from '../../lib/normalize'
export const getAllProductsQuery = /* GraphQL */ `
query getAllProducts(
@@ -50,83 +56,80 @@ export type GetAllProductsResult<
}
> = T
-const FIELDS = [
- 'products',
- 'featuredProducts',
- 'bestSellingProducts',
- 'newestProducts',
-]
-
-export type ProductTypes =
- | 'products'
- | 'featuredProducts'
- | 'bestSellingProducts'
- | 'newestProducts'
-
-export type ProductVariables = { field?: ProductTypes } & Omit<
- GetAllProductsQueryVariables,
- ProductTypes | 'hasLocale'
->
-
-async function getAllProducts(opts?: {
- variables?: ProductVariables
- config?: BigcommerceConfig
- preview?: boolean
-}): Promise
-
-async function getAllProducts<
- T extends Record,
- V = any
->(opts: {
- query: string
- variables?: V
- config?: BigcommerceConfig
- preview?: boolean
-}): Promise>
-
-async function getAllProducts({
- query = getAllProductsQuery,
- variables: { field = 'products', ...vars } = {},
- config,
-}: {
- query?: string
- variables?: ProductVariables
- config?: BigcommerceConfig
- preview?: boolean
-} = {}): Promise {
- config = getConfig(config)
-
- const locale = vars.locale || config.locale
- const variables: GetAllProductsQueryVariables = {
- ...vars,
- locale,
- hasLocale: !!locale,
+function getProductsType(
+ relevance?: GetAllProductsOperation['variables']['relevance']
+) {
+ switch (relevance) {
+ case 'featured':
+ return 'featuredProducts'
+ case 'best_selling':
+ return 'bestSellingProducts'
+ case 'newest':
+ return 'newestProducts'
+ default:
+ return 'products'
}
-
- if (!FIELDS.includes(field)) {
- throw new Error(
- `The field variable has to match one of ${FIELDS.join(', ')}`
- )
- }
-
- variables[field] = true
-
- // RecursivePartial forces the method to check for every prop in the data, which is
- // required in case there's a custom `query`
- const { data } = await config.fetch>(
- query,
- { variables }
- )
- const edges = data.site?.[field]?.edges
- const products = filterEdges(edges as RecursiveRequired)
-
- if (locale && config.applyLocale) {
- products.forEach((product: RecursivePartial) => {
- if (product.node) setProductLocaleMeta(product.node)
- })
- }
-
- return { products }
}
-export default getAllProducts
+export default function getAllProductsOperation({
+ commerce,
+}: OperationContext) {
+ async function getAllProducts(opts?: {
+ variables?: T['variables']
+ config?: Partial
+ preview?: boolean
+ }): Promise
+
+ async function getAllProducts(
+ opts: {
+ variables?: T['variables']
+ config?: Partial
+ preview?: boolean
+ } & OperationOptions
+ ): Promise
+
+ async function getAllProducts({
+ query = getAllProductsQuery,
+ variables: vars = {},
+ config: cfg,
+ }: {
+ query?: string
+ variables?: T['variables']
+ config?: Partial
+ preview?: boolean
+ } = {}): Promise {
+ const config = commerce.getConfig(cfg)
+ const { locale } = config
+ const field = getProductsType(vars.relevance)
+ const variables: GetAllProductsQueryVariables = {
+ locale,
+ hasLocale: !!locale,
+ }
+
+ variables[field] = true
+
+ if (vars.first) variables.first = vars.first
+ if (vars.ids) variables.entityIds = vars.ids.map((id) => Number(id))
+
+ // RecursivePartial forces the method to check for every prop in the data, which is
+ // required in case there's a custom `query`
+ const { data } = await config.fetch>(
+ query,
+ { variables }
+ )
+ const edges = data.site?.[field]?.edges
+ const products = filterEdges(edges as RecursiveRequired)
+
+ if (locale && config.applyLocale) {
+ products.forEach((product: RecursivePartial) => {
+ if (product.node) setProductLocaleMeta(product.node)
+ })
+ }
+
+ return {
+ products: products.map(({ node }) => normalizeProduct(node as any)),
+ }
+ }
+
+ return getAllProducts
+}
diff --git a/framework/bigcommerce/api/operations/get-customer-wishlist.ts b/framework/bigcommerce/api/operations/get-customer-wishlist.ts
index 2c1299b46..fc9487ffe 100644
--- a/framework/bigcommerce/api/operations/get-customer-wishlist.ts
+++ b/framework/bigcommerce/api/operations/get-customer-wishlist.ts
@@ -1,87 +1,81 @@
+import type {
+ OperationContext,
+ OperationOptions,
+} from '@commerce/api/operations'
+import type {
+ GetCustomerWishlistOperation,
+ Wishlist,
+} from '../../types/wishlist'
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
-import { definitions } from '../definitions/wishlist'
-import { BigcommerceConfig, getConfig } from '..'
+import { BigcommerceConfig, Provider } from '..'
import getAllProducts, { ProductEdge } from './get-all-products'
-export type Wishlist = Omit & {
- items?: WishlistItem[]
-}
+export default function getCustomerWishlistOperation({
+ commerce,
+}: OperationContext) {
+ async function getCustomerWishlist<
+ T extends GetCustomerWishlistOperation
+ >(opts: {
+ variables: T['variables']
+ config?: BigcommerceConfig
+ includeProducts?: boolean
+ }): Promise
-export type WishlistItem = NonNullable<
- definitions['wishlist_Full']['items']
->[0] & {
- product?: ProductEdge['node']
-}
+ async function getCustomerWishlist(
+ opts: {
+ variables: T['variables']
+ config?: BigcommerceConfig
+ includeProducts?: boolean
+ } & OperationOptions
+ ): Promise
-export type GetCustomerWishlistResult<
- T extends { wishlist?: any } = { wishlist?: Wishlist }
-> = T
+ async function getCustomerWishlist({
+ config,
+ variables,
+ includeProducts,
+ }: {
+ url?: string
+ variables: T['variables']
+ config?: BigcommerceConfig
+ includeProducts?: boolean
+ }): Promise {
+ config = commerce.getConfig(config)
-export type GetCustomerWishlistVariables = {
- customerId: number
-}
+ const { data = [] } = await config.storeApiFetch<
+ RecursivePartial<{ data: Wishlist[] }>
+ >(`/v3/wishlists?customer_id=${variables.customerId}`)
+ const wishlist = data[0]
-async function getCustomerWishlist(opts: {
- variables: GetCustomerWishlistVariables
- config?: BigcommerceConfig
- includeProducts?: boolean
-}): Promise
+ if (includeProducts && wishlist?.items?.length) {
+ const ids = wishlist.items
+ ?.map((item) => (item?.product_id ? String(item?.product_id) : null))
+ .filter((id): id is string => !!id)
-async function getCustomerWishlist<
- T extends { wishlist?: any },
- V = any
->(opts: {
- url: string
- variables: V
- config?: BigcommerceConfig
- includeProducts?: boolean
-}): Promise>
-
-async function getCustomerWishlist({
- config,
- variables,
- includeProducts,
-}: {
- url?: string
- variables: GetCustomerWishlistVariables
- config?: BigcommerceConfig
- includeProducts?: boolean
-}): Promise {
- config = getConfig(config)
-
- const { data = [] } = await config.storeApiFetch<
- RecursivePartial<{ data: Wishlist[] }>
- >(`/v3/wishlists?customer_id=${variables.customerId}`)
- const wishlist = data[0]
-
- if (includeProducts && wishlist?.items?.length) {
- const entityIds = wishlist.items
- ?.map((item) => item?.product_id)
- .filter((id): id is number => !!id)
-
- if (entityIds?.length) {
- const graphqlData = await getAllProducts({
- variables: { first: 100, entityIds },
- config,
- })
- // Put the products in an object that we can use to get them by id
- const productsById = graphqlData.products.reduce<{
- [k: number]: ProductEdge
- }>((prods, p) => {
- prods[p.node.entityId] = p
- return prods
- }, {})
- // Populate the wishlist items with the graphql products
- wishlist.items.forEach((item) => {
- const product = item && productsById[item.product_id!]
- if (item && product) {
- item.product = product.node
- }
- })
+ if (ids?.length) {
+ const graphqlData = await commerce.getAllProducts({
+ variables: { first: 100, ids },
+ config,
+ })
+ // Put the products in an object that we can use to get them by id
+ const productsById = graphqlData.products.reduce<{
+ [k: number]: ProductEdge
+ }>((prods, p) => {
+ prods[Number(p.id)] = p as any
+ return prods
+ }, {})
+ // Populate the wishlist items with the graphql products
+ wishlist.items.forEach((item) => {
+ const product = item && productsById[item.product_id!]
+ if (item && product) {
+ // @ts-ignore Fix this type when the wishlist type is properly defined
+ item.product = product
+ }
+ })
+ }
}
+
+ return { wishlist: wishlist as RecursiveRequired }
}
- return { wishlist: wishlist as RecursiveRequired }
+ return getCustomerWishlist
}
-
-export default getCustomerWishlist
diff --git a/framework/bigcommerce/api/operations/get-page.ts b/framework/bigcommerce/api/operations/get-page.ts
index 3010dd34c..6a1fea9d0 100644
--- a/framework/bigcommerce/api/operations/get-page.ts
+++ b/framework/bigcommerce/api/operations/get-page.ts
@@ -1,53 +1,54 @@
+import type {
+ OperationContext,
+ OperationOptions,
+} from '@commerce/api/operations'
+import type { GetPageOperation, Page } from '../../types/page'
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
-import { BigcommerceConfig, getConfig } from '..'
-import { definitions } from '../definitions/store-content'
+import type { BigcommerceConfig, Provider } from '..'
+import { normalizePage } from '../../lib/normalize'
-export type Page = definitions['page_Full']
+export default function getPageOperation({
+ commerce,
+}: OperationContext) {
+ async function getPage(opts: {
+ variables: T['variables']
+ config?: Partial
+ preview?: boolean
+ }): Promise
-export type GetPageResult = T
+ async function getPage(
+ opts: {
+ variables: T['variables']
+ config?: Partial
+ preview?: boolean
+ } & OperationOptions
+ ): Promise
-export type PageVariables = {
- id: number
-}
+ async function getPage({
+ url,
+ variables,
+ config,
+ preview,
+ }: {
+ url?: string
+ variables: T['variables']
+ config?: Partial