diff --git a/packages/bigcommerce/package.json b/packages/bigcommerce/package.json index b288317c7..551d0ea68 100644 --- a/packages/bigcommerce/package.json +++ b/packages/bigcommerce/package.json @@ -47,14 +47,15 @@ } }, "dependencies": { + "@cfworker/uuid": "^1.12.4", + "@tsndr/cloudflare-worker-jwt": "^2.1.0", "@vercel/commerce": "workspace:*", - "@vercel/fetch": "^6.2.0", "cookie": "^0.4.1", "immutability-helper": "^3.1.1", + "js-cookie": "^3.0.1", "jsonwebtoken": "^8.5.1", "lodash.debounce": "^4.0.8", - "uuidv4": "^6.2.12", - "node-fetch": "^2.6.7" + "uuidv4": "^6.2.13" }, "peerDependencies": { "next": "^12", @@ -69,8 +70,8 @@ "@types/jsonwebtoken": "^8.5.7", "@types/lodash.debounce": "^4.0.6", "@types/node": "^17.0.8", - "@types/react": "^18.0.14", "@types/node-fetch": "^2.6.2", + "@types/react": "^18.0.14", "lint-staged": "^12.1.7", "next": "^12.0.8", "prettier": "^2.5.1", diff --git a/packages/bigcommerce/src/api/endpoints/cart/add-item.ts b/packages/bigcommerce/src/api/endpoints/cart/add-item.ts index acd656a77..bfb0cfe0b 100644 --- a/packages/bigcommerce/src/api/endpoints/cart/add-item.ts +++ b/packages/bigcommerce/src/api/endpoints/cart/add-item.ts @@ -1,22 +1,14 @@ -// @ts-nocheck +import type { CartEndpoint } from '.' +import type { BigcommerceCart } from '../../../types' + import { normalizeCart } from '../../../lib/normalize' import { parseCartItem } from '../../utils/parse-item' import getCartCookie from '../../utils/get-cart-cookie' -import type { CartEndpoint } from '.' const addItem: CartEndpoint['handlers']['addItem'] = async ({ - res, body: { cartId, item }, config, }) => { - if (!item) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Missing item' }], - }) - } - if (!item.quantity) item.quantity = 1 - const options = { method: 'POST', body: JSON.stringify({ @@ -26,22 +18,27 @@ const addItem: CartEndpoint['handlers']['addItem'] = async ({ : {}), }), } + const { data } = cartId - ? await config.storeApiFetch( + ? await config.storeApiFetch<{ data: BigcommerceCart }>( `/v3/carts/${cartId}/items?include=line_items.physical_items.options,line_items.digital_items.options`, options ) - : await config.storeApiFetch( + : await config.storeApiFetch<{ data: BigcommerceCart }>( '/v3/carts?include=line_items.physical_items.options,line_items.digital_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: normalizeCart(data) }) + return { + data: normalizeCart(data), + headers: { + 'Set-Cookie': getCartCookie( + config.cartCookie, + data.id, + config.cartCookieMaxAge + ), + }, + } } export default addItem diff --git a/packages/bigcommerce/src/api/endpoints/cart/get-cart.ts b/packages/bigcommerce/src/api/endpoints/cart/get-cart.ts index 5b48119ca..4fcf2b190 100644 --- a/packages/bigcommerce/src/api/endpoints/cart/get-cart.ts +++ b/packages/bigcommerce/src/api/endpoints/cart/get-cart.ts @@ -1,36 +1,41 @@ -// @ts-nocheck +import type { CartEndpoint } from '.' +import type { BigcommerceCart } from '../../../types' + +import getCartCookie from '../../utils/get-cart-cookie' + import { normalizeCart } from '../../../lib/normalize' import { BigcommerceApiError } from '../../utils/errors' -import getCartCookie from '../../utils/get-cart-cookie' -import type { BigcommerceCart } from '../../../types' -import type { CartEndpoint } from '.' // Return current cart info const getCart: CartEndpoint['handlers']['getCart'] = async ({ - res, body: { cartId }, config, }) => { - let result: { data?: BigcommerceCart } = {} - if (cartId) { try { - result = await config.storeApiFetch( + const result = await config.storeApiFetch<{ + data?: BigcommerceCart + } | null>( `/v3/carts/${cartId}?include=line_items.physical_items.options,line_items.digital_items.options` ) + + return { + data: result?.data ? normalizeCart(result.data) : null, + } } catch (error) { if (error instanceof BigcommerceApiError && error.status === 404) { - // Remove the cookie if it exists but the cart wasn't found - res.setHeader('Set-Cookie', getCartCookie(config.cartCookie)) + return { + headers: { 'Set-Cookie': getCartCookie(config.cartCookie) }, + } } else { throw error } } } - res.status(200).json({ - data: result.data ? normalizeCart(result.data) : null, - }) + return { + data: null, + } } export default getCart diff --git a/packages/bigcommerce/src/api/endpoints/cart/index.ts b/packages/bigcommerce/src/api/endpoints/cart/index.ts index 92601e189..bd2fbfe65 100644 --- a/packages/bigcommerce/src/api/endpoints/cart/index.ts +++ b/packages/bigcommerce/src/api/endpoints/cart/index.ts @@ -1,4 +1,4 @@ -import { GetAPISchema, createEndpoint } from '@vercel/commerce/api' +import { type GetAPISchema, createEndpoint } from '@vercel/commerce/api' import cartEndpoint from '@vercel/commerce/api/endpoints/cart' import type { CartSchema } from '@vercel/commerce/types/cart' import type { BigcommerceAPI } from '../..' diff --git a/packages/bigcommerce/src/api/endpoints/cart/remove-item.ts b/packages/bigcommerce/src/api/endpoints/cart/remove-item.ts index baf10c80f..c3f6c41c6 100644 --- a/packages/bigcommerce/src/api/endpoints/cart/remove-item.ts +++ b/packages/bigcommerce/src/api/endpoints/cart/remove-item.ts @@ -1,34 +1,26 @@ -import { normalizeCart } from '../../../lib/normalize' -import getCartCookie from '../../utils/get-cart-cookie' import type { CartEndpoint } from '.' +import { normalizeCart } from '../../../lib/normalize' +import getCartCookie from '../../utils/get-cart-cookie' + const removeItem: CartEndpoint['handlers']['removeItem'] = async ({ - res, body: { cartId, itemId }, config, }) => { - if (!cartId || !itemId) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) - } - const result = await config.storeApiFetch<{ data: any } | null>( `/v3/carts/${cartId}/items/${itemId}?include=line_items.physical_items.options`, { method: 'DELETE' } ) - const data = result?.data ?? null - - res.setHeader( - 'Set-Cookie', - data - ? // Update the cart cookie - getCartCookie(config.cartCookie, cartId, config.cartCookieMaxAge) - : // Remove the cart cookie if the cart was removed (empty items) - getCartCookie(config.cartCookie) - ) - res.status(200).json({ data: data && normalizeCart(data) }) + return { + data: result?.data ? normalizeCart(result.data) : null, + headers: { + 'Set-Cookie': result?.data + ? // Update the cart cookie + getCartCookie(config.cartCookie, cartId, config.cartCookieMaxAge) + : // Remove the cart cookie if the cart was removed (empty items) + getCartCookie(config.cartCookie), + }, + } } export default removeItem diff --git a/packages/bigcommerce/src/api/endpoints/cart/update-item.ts b/packages/bigcommerce/src/api/endpoints/cart/update-item.ts index 113553fad..33a65be88 100644 --- a/packages/bigcommerce/src/api/endpoints/cart/update-item.ts +++ b/packages/bigcommerce/src/api/endpoints/cart/update-item.ts @@ -1,21 +1,15 @@ +import type { CartEndpoint } from '.' +import type { BigcommerceCart } from '../../../types' + import { normalizeCart } from '../../../lib/normalize' import { parseCartItem } from '../../utils/parse-item' import getCartCookie from '../../utils/get-cart-cookie' -import type { CartEndpoint } from '.' const updateItem: CartEndpoint['handlers']['updateItem'] = async ({ - res, body: { cartId, itemId, item }, config, }) => { - if (!cartId || !itemId || !item) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) - } - - const { data } = await config.storeApiFetch( + const { data } = await config.storeApiFetch<{ data: BigcommerceCart }>( `/v3/carts/${cartId}/items/${itemId}?include=line_items.physical_items.options`, { method: 'PUT', @@ -25,12 +19,16 @@ const updateItem: CartEndpoint['handlers']['updateItem'] = async ({ } ) - // Update the cart cookie - res.setHeader( - 'Set-Cookie', - getCartCookie(config.cartCookie, cartId, config.cartCookieMaxAge) - ) - res.status(200).json({ data: normalizeCart(data) }) + return { + data: normalizeCart(data), + headers: { + 'Set-Cookie': getCartCookie( + config.cartCookie, + cartId, + config.cartCookieMaxAge + ), + }, + } } export default updateItem diff --git a/packages/bigcommerce/src/api/endpoints/catalog/products/get-products.ts b/packages/bigcommerce/src/api/endpoints/catalog/products/get-products.ts index f88a231e1..9b067ca4a 100644 --- a/packages/bigcommerce/src/api/endpoints/catalog/products/get-products.ts +++ b/packages/bigcommerce/src/api/endpoints/catalog/products/get-products.ts @@ -11,7 +11,6 @@ const LIMIT = 12 // Return current cart info const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({ - res, body: { search, categoryId, brandId, sort }, config, commerce, @@ -73,7 +72,7 @@ const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({ if (product) products.push(product) }) - res.status(200).json({ data: { products, found } }) + return { data: { products, found } } } export default getProducts diff --git a/packages/bigcommerce/src/api/endpoints/checkout/get-checkout.ts b/packages/bigcommerce/src/api/endpoints/checkout/get-checkout.ts index e98e6ca54..ce39ee29c 100644 --- a/packages/bigcommerce/src/api/endpoints/checkout/get-checkout.ts +++ b/packages/bigcommerce/src/api/endpoints/checkout/get-checkout.ts @@ -1,38 +1,47 @@ import type { CheckoutEndpoint } from '.' import getCustomerId from '../../utils/get-customer-id' -import jwt from 'jsonwebtoken' -import { uuid } from 'uuidv4' const fullCheckout = true const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ req, - res, config, }) => { const { cookies } = req - const cartId = cookies[config.cartCookie] - const customerToken = cookies[config.customerCookie] + const cartId = cookies.get(config.cartCookie) + const customerToken = cookies.get(config.customerCookie) + if (!cartId) { - res.redirect('/cart') - return + return { redirectTo: '/cart' } } - const { data } = await config.storeApiFetch( + + const { data } = await config.storeApiFetch( `/v3/carts/${cartId}/redirect_urls`, { method: 'POST', } ) + const customerId = customerToken && (await getCustomerId({ customerToken, config })) //if there is a customer create a jwt token if (!customerId) { if (fullCheckout) { - res.redirect(data.checkout_url) - return + return { redirectTo: data.checkout_url } } } else { + // Dynamically import uuid & jsonwebtoken based on the runtime + const { uuid } = + process.env.NEXT_RUNTIME === 'edge' + ? await import('@cfworker/uuid') + : await import('uuidv4') + + const jwt = + process.env.NEXT_RUNTIME === 'edge' + ? await import('@tsndr/cloudflare-worker-jwt') + : await import('jsonwebtoken') + const dateCreated = Math.round(new Date().getTime() / 1000) const payload = { iss: config.storeApiClientId, @@ -42,49 +51,51 @@ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ store_hash: config.storeHash, customer_id: customerId, channel_id: config.storeChannelId, - redirect_to: data.checkout_url.replace(config.storeUrl, ""), + redirect_to: data.checkout_url.replace(config.storeUrl, ''), } let token = jwt.sign(payload, config.storeApiClientSecret!, { algorithm: 'HS256', }) let checkouturl = `${config.storeUrl}/login/token/${token}` - console.log('checkouturl', checkouturl) + if (fullCheckout) { - res.redirect(checkouturl) - return + return { redirectTo: checkouturl } } } // TODO: make the embedded checkout work too! - const html = ` - - - - - - Checkout - - - - -
- - - ` + // const html = ` + // + // + // + // + // + // Checkout + // + // + // + // + //
+ // + // + // ` - res.status(200) - res.setHeader('Content-Type', 'text/html') - res.write(html) - res.end() + // return new Response(html, { + // headers: { + // 'Content-Type': 'text/html', + // }, + // }) + + return { data: null } } export default getCheckout diff --git a/packages/bigcommerce/src/api/endpoints/customer/address.ts b/packages/bigcommerce/src/api/endpoints/customer/address.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/bigcommerce/src/api/endpoints/customer/address.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/bigcommerce/src/api/endpoints/customer/card.ts b/packages/bigcommerce/src/api/endpoints/customer/card.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/bigcommerce/src/api/endpoints/customer/card.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/bigcommerce/src/api/endpoints/customer/get-logged-in-customer.ts b/packages/bigcommerce/src/api/endpoints/customer/get-logged-in-customer.ts index e517072ae..9076ab584 100644 --- a/packages/bigcommerce/src/api/endpoints/customer/get-logged-in-customer.ts +++ b/packages/bigcommerce/src/api/endpoints/customer/get-logged-in-customer.ts @@ -1,5 +1,6 @@ import type { GetLoggedInCustomerQuery } from '../../../../schema' import type { CustomerEndpoint } from '.' +import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' export const getLoggedInCustomerQuery = /* GraphQL */ ` query getLoggedInCustomer { @@ -25,29 +26,26 @@ export const getLoggedInCustomerQuery = /* GraphQL */ ` export type Customer = NonNullable const getLoggedInCustomer: CustomerEndpoint['handlers']['getLoggedInCustomer'] = - async ({ req, res, config }) => { - const token = req.cookies[config.customerCookie] + async ({ req, config }) => { + const token = req.cookies.get(config.customerCookie) if (token) { const { data } = await config.fetch( getLoggedInCustomerQuery, undefined, { - headers: { - cookie: `${config.customerCookie}=${token}`, - }, + 'Set-Cookie': `${config.customerCookie}=${token}`, } ) const { customer } = data if (!customer) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Customer not found', code: 'not_found' }], + throw new CommerceAPIError('Customer not found', { + status: 404, }) } - return res.status(200).json({ + return { data: { customer: { id: String(customer.entityId), @@ -59,10 +57,12 @@ const getLoggedInCustomer: CustomerEndpoint['handlers']['getLoggedInCustomer'] = notes: customer.notes, }, }, - }) + } } - res.status(200).json({ data: null }) + return { + data: null, + } } export default getLoggedInCustomer diff --git a/packages/bigcommerce/src/api/endpoints/index.ts b/packages/bigcommerce/src/api/endpoints/index.ts new file mode 100644 index 000000000..32f142a9b --- /dev/null +++ b/packages/bigcommerce/src/api/endpoints/index.ts @@ -0,0 +1,27 @@ +import type { BigcommerceAPI, Provider } from '..' + +import createEndpoints from '@vercel/commerce/api/endpoints' + +import cart from './cart' +import login from './login' +import logout from './logout' +import signup from './signup' +import checkout from './checkout' +import customer from './customer' +import wishlist from './wishlist' +import products from './catalog/products' + +const endpoints = { + cart, + login, + logout, + signup, + checkout, + wishlist, + customer, + 'catalog/products': products, +} + +export default function bigcommerceAPI(commerce: BigcommerceAPI) { + return createEndpoints(commerce, endpoints) +} diff --git a/packages/bigcommerce/src/api/endpoints/login/login.ts b/packages/bigcommerce/src/api/endpoints/login/login.ts index 0ebc5b110..3ad1419aa 100644 --- a/packages/bigcommerce/src/api/endpoints/login/login.ts +++ b/packages/bigcommerce/src/api/endpoints/login/login.ts @@ -1,49 +1,35 @@ -import { FetcherError } from '@vercel/commerce/utils/errors' import type { LoginEndpoint } from '.' +import { FetcherError } from '@vercel/commerce/utils/errors' +import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' + const invalidCredentials = /invalid credentials/i const login: LoginEndpoint['handlers']['login'] = async ({ - res, body: { email, password }, config, commerce, }) => { - // TODO: Add proper validations with something like Ajv - if (!(email && password)) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) - } - // TODO: validate the password and email - // Passwords must be at least 7 characters and contain both alphabetic - // and numeric characters. - try { + const res = new Response() await commerce.login({ variables: { email, password }, config, res }) + return { + status: res.status, + headers: res.headers, + } } catch (error) { // Check if the email and password didn't match an existing account - if ( - error instanceof FetcherError && - invalidCredentials.test(error.message) - ) { - return res.status(401).json({ - data: null, - errors: [ - { - message: - 'Cannot find an account that matches the provided credentials', - code: 'invalid_credentials', - }, - ], - }) + if (error instanceof FetcherError) { + throw new CommerceAPIError( + invalidCredentials.test(error.message) + ? 'Cannot find an account that matches the provided credentials' + : error.message, + { status: error.status || 401 } + ) + } else { + throw error } - - throw error } - - res.status(200).json({ data: null }) } export default login diff --git a/packages/bigcommerce/src/api/endpoints/logout/logout.ts b/packages/bigcommerce/src/api/endpoints/logout/logout.ts index b90a8c3ce..51e63f900 100644 --- a/packages/bigcommerce/src/api/endpoints/logout/logout.ts +++ b/packages/bigcommerce/src/api/endpoints/logout/logout.ts @@ -2,22 +2,24 @@ import { serialize } from 'cookie' import type { LogoutEndpoint } from '.' const logout: LogoutEndpoint['handlers']['logout'] = async ({ - res, body: { redirectTo }, config, }) => { - // Remove the cookie - res.setHeader( - 'Set-Cookie', - serialize(config.customerCookie, '', { maxAge: -1, path: '/' }) - ) - - // Only allow redirects to a relative URL - if (redirectTo?.startsWith('/')) { - res.redirect(redirectTo) - } else { - res.status(200).json({ data: null }) + const headers = { + 'Set-Cookie': serialize(config.customerCookie, '', { + maxAge: -1, + path: '/', + }), } + + return redirectTo + ? { + redirectTo, + headers, + } + : { + headers, + } } export default logout diff --git a/packages/bigcommerce/src/api/endpoints/signup/signup.ts b/packages/bigcommerce/src/api/endpoints/signup/signup.ts index 46c071be8..94499f96a 100644 --- a/packages/bigcommerce/src/api/endpoints/signup/signup.ts +++ b/packages/bigcommerce/src/api/endpoints/signup/signup.ts @@ -1,23 +1,13 @@ -import { BigcommerceApiError } from '../../utils/errors' import type { SignupEndpoint } from '.' +import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' + +import { BigcommerceApiError } from '../../utils/errors' 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)) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) - } - // TODO: validate the password and email - // Passwords must be at least 7 characters and contain both alphabetic - // and numeric characters. - try { await config.storeApiFetch('/v3/customers', { method: 'POST', @@ -35,28 +25,26 @@ const signup: SignupEndpoint['handlers']['signup'] = async ({ } catch (error) { if (error instanceof BigcommerceApiError && error.status === 422) { const hasEmailError = '0.email' in error.data?.errors - // If there's an error with the email, it most likely means it's duplicated if (hasEmailError) { - return res.status(400).json({ - data: null, - errors: [ - { - message: 'The email is already in use', - code: 'duplicated_email', - }, - ], + throw new CommerceAPIError('Email already in use', { + status: 400, + code: 'duplicated_email', }) } + } else { + throw error } - - throw error } + const res = new Response() + // Login the customer right after creating it await commerce.login({ variables: { email, password }, res, config }) - res.status(200).json({ data: null }) + return { + headers: res.headers, + } } export default signup diff --git a/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts b/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts index 734a64402..cc1cdf951 100644 --- a/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts +++ b/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts @@ -1,67 +1,53 @@ -import getCustomerWishlist from '../../operations/get-customer-wishlist' import { parseWishlistItem } from '../../utils/parse-item' import getCustomerId from '../../utils/get-customer-id' import type { WishlistEndpoint } from '.' const addItem: WishlistEndpoint['handlers']['addItem'] = async ({ - res, body: { customerToken, item }, config, commerce, }) => { - if (!item) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Missing item' }], - }) + const customerId = + customerToken && (await getCustomerId({ customerToken, config })) + + if (!customerId) { + throw new Error('Invalid request. No CustomerId') } - try { - const customerId = - customerToken && (await getCustomerId({ customerToken, config })) + let { wishlist } = await commerce.getCustomerWishlist({ + variables: { customerId }, + config, + }) - if (!customerId) { - throw new Error('Invalid request. No CustomerId') - } - - let { wishlist } = await commerce.getCustomerWishlist({ - variables: { customerId }, - config, + if (!wishlist) { + // If user has no wishlist, then let's create one with new item + const { data } = await config.storeApiFetch('/v3/wishlists', { + method: 'POST', + body: JSON.stringify({ + name: 'Next.js Commerce Wishlist', + is_public: false, + customer_id: Number(customerId), + items: [parseWishlistItem(item)], + }), }) - - if (!wishlist) { - // If user has no wishlist, then let's create one with new item - const { data } = await config.storeApiFetch('/v3/wishlists', { - method: 'POST', - body: JSON.stringify({ - name: 'Next.js Commerce Wishlist', - is_public: false, - customer_id: Number(customerId), - items: [parseWishlistItem(item)], - }), - }) - return res.status(200).json(data) + return { + data, } - - // Existing Wishlist, let's add Item to Wishlist - const { data } = await config.storeApiFetch( - `/v3/wishlists/${wishlist.id}/items`, - { - method: 'POST', - body: JSON.stringify({ - items: [parseWishlistItem(item)], - }), - } - ) - - // Returns Wishlist - return res.status(200).json(data) - } catch (err: any) { - res.status(500).json({ - data: null, - errors: [{ message: err.message }], - }) } + + // Existing Wishlist, let's add Item to Wishlist + const { data } = await config.storeApiFetch( + `/v3/wishlists/${wishlist.id}/items`, + { + method: 'POST', + body: JSON.stringify({ + items: [parseWishlistItem(item)], + }), + } + ) + + // Returns Wishlist + return { data } } export default addItem diff --git a/packages/bigcommerce/src/api/endpoints/wishlist/get-wishlist.ts b/packages/bigcommerce/src/api/endpoints/wishlist/get-wishlist.ts index 82f468195..c0f57fb92 100644 --- a/packages/bigcommerce/src/api/endpoints/wishlist/get-wishlist.ts +++ b/packages/bigcommerce/src/api/endpoints/wishlist/get-wishlist.ts @@ -1,10 +1,10 @@ +import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' import type { Wishlist } from '@vercel/commerce/types/wishlist' import type { WishlistEndpoint } from '.' import getCustomerId from '../../utils/get-customer-id' // Return wishlist info const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({ - res, body: { customerToken, includeProducts }, config, commerce, @@ -16,11 +16,7 @@ const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({ customerToken && (await getCustomerId({ customerToken, config })) if (!customerId) { - // If the customerToken is invalid, then this request is too - return res.status(404).json({ - data: null, - errors: [{ message: 'Wishlist not found' }], - }) + throw new CommerceAPIError('Wishlist not found', { status: 404 }) } const { wishlist } = await commerce.getCustomerWishlist({ @@ -32,7 +28,7 @@ const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({ result = { data: wishlist } } - res.status(200).json({ data: result.data ?? null }) + return { data: result.data ?? null } } export default getWishlist diff --git a/packages/bigcommerce/src/api/endpoints/wishlist/remove-item.ts b/packages/bigcommerce/src/api/endpoints/wishlist/remove-item.ts index 17b3f6dec..36b0a09db 100644 --- a/packages/bigcommerce/src/api/endpoints/wishlist/remove-item.ts +++ b/packages/bigcommerce/src/api/endpoints/wishlist/remove-item.ts @@ -1,10 +1,10 @@ import type { Wishlist } from '@vercel/commerce/types/wishlist' import getCustomerId from '../../utils/get-customer-id' import type { WishlistEndpoint } from '.' +import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' // Return wishlist info const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({ - res, body: { customerToken, itemId }, config, commerce, @@ -20,10 +20,7 @@ const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({ {} if (!wishlist || !itemId) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) + throw new CommerceAPIError('Wishlist not found', { status: 400 }) } const result = await config.storeApiFetch<{ data: Wishlist } | null>( @@ -32,7 +29,7 @@ const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({ ) const data = result?.data ?? null - res.status(200).json({ data }) + return { data } } export default removeItem diff --git a/packages/bigcommerce/src/api/index.ts b/packages/bigcommerce/src/api/index.ts index bd5fcb3b7..98f61d6d4 100644 --- a/packages/bigcommerce/src/api/index.ts +++ b/packages/bigcommerce/src/api/index.ts @@ -1,4 +1,3 @@ -import type { RequestInit } from '@vercel/fetch' import { CommerceAPI, CommerceAPIConfig, @@ -35,7 +34,14 @@ export interface BigcommerceConfig extends CommerceAPIConfig { storeUrl?: string storeApiClientSecret?: string storeHash?: string - storeApiFetch(endpoint: string, options?: RequestInit): Promise + storeApiFetch( + endpoint: string, + options?: { + method?: string + body?: any + headers?: HeadersInit + } + ): Promise } const API_URL = process.env.BIGCOMMERCE_STOREFRONT_API_URL // GraphAPI diff --git a/packages/bigcommerce/src/api/operations/login.ts b/packages/bigcommerce/src/api/operations/login.ts index 4a33741f8..dad3b48b1 100644 --- a/packages/bigcommerce/src/api/operations/login.ts +++ b/packages/bigcommerce/src/api/operations/login.ts @@ -1,4 +1,3 @@ -import type { ServerResponse } from 'http' import type { OperationContext, OperationOptions, @@ -23,14 +22,14 @@ export default function loginOperation({ async function login(opts: { variables: T['variables'] config?: BigcommerceConfig - res: ServerResponse + res: Response }): Promise async function login( opts: { variables: T['variables'] config?: BigcommerceConfig - res: ServerResponse + res: Response } & OperationOptions ): Promise @@ -42,7 +41,7 @@ export default function loginOperation({ }: { query?: string variables: T['variables'] - res: ServerResponse + res: Response config?: BigcommerceConfig }): Promise { config = commerce.getConfig(config) @@ -64,10 +63,15 @@ export default function loginOperation({ cookie = cookie.replace(/; SameSite=none/gi, '; SameSite=lax') } - response.setHeader( - 'Set-Cookie', - concatHeader(response.getHeader('Set-Cookie'), cookie)! - ) + const prevCookie = response.headers.get('Set-Cookie') + const newCookie = concatHeader(prevCookie, cookie) + + if (newCookie) { + res.headers.set( + 'Set-Cookie', + String(Array.isArray(newCookie) ? newCookie.join(',') : newCookie) + ) + } } return { diff --git a/packages/bigcommerce/src/api/utils/concat-cookie.ts b/packages/bigcommerce/src/api/utils/concat-cookie.ts index 362e12e99..7d4e0d429 100644 --- a/packages/bigcommerce/src/api/utils/concat-cookie.ts +++ b/packages/bigcommerce/src/api/utils/concat-cookie.ts @@ -1,4 +1,4 @@ -type Header = string | number | string[] | undefined +type Header = string | number | string[] | undefined | null export default function concatHeader(prev: Header, val: Header) { if (!val) return prev diff --git a/packages/bigcommerce/src/api/utils/errors.ts b/packages/bigcommerce/src/api/utils/errors.ts index 77e2007fc..bd7a995d8 100644 --- a/packages/bigcommerce/src/api/utils/errors.ts +++ b/packages/bigcommerce/src/api/utils/errors.ts @@ -1,5 +1,3 @@ -import type { Response } from '@vercel/fetch' - // Used for GraphQL errors export class BigcommerceGraphQLError extends Error {} diff --git a/packages/bigcommerce/src/api/utils/fetch-graphql-api.ts b/packages/bigcommerce/src/api/utils/fetch-graphql-api.ts index ad385fee6..9072b2432 100644 --- a/packages/bigcommerce/src/api/utils/fetch-graphql-api.ts +++ b/packages/bigcommerce/src/api/utils/fetch-graphql-api.ts @@ -1,19 +1,22 @@ import { FetcherError } from '@vercel/commerce/utils/errors' import type { GraphQLFetcher } from '@vercel/commerce/api' import type { BigcommerceConfig } from '../index' -import fetch from './fetch' const fetchGraphqlApi: (getConfig: () => BigcommerceConfig) => GraphQLFetcher = (getConfig) => - async (query: string, { variables, preview } = {}, fetchOptions) => { + async ( + query: string, + { variables, preview } = {}, + options: { headers?: HeadersInit } = {} + ): Promise => { // log.warn(query) const config = getConfig() + const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { - ...fetchOptions, method: 'POST', headers: { Authorization: `Bearer ${config.apiToken}`, - ...fetchOptions?.headers, + ...options.headers, 'Content-Type': 'application/json', }, body: JSON.stringify({ diff --git a/packages/bigcommerce/src/api/utils/fetch-store-api.ts b/packages/bigcommerce/src/api/utils/fetch-store-api.ts index a44e8957b..76193f424 100644 --- a/packages/bigcommerce/src/api/utils/fetch-store-api.ts +++ b/packages/bigcommerce/src/api/utils/fetch-store-api.ts @@ -1,11 +1,16 @@ -import type { FetchOptions, Response } from '@vercel/fetch' import type { BigcommerceConfig } from '../index' import { BigcommerceApiError, BigcommerceNetworkError } from './errors' -import fetch from './fetch' const fetchStoreApi = (getConfig: () => BigcommerceConfig) => - async (endpoint: string, options?: FetchOptions): Promise => { + async ( + endpoint: string, + options?: { + method?: string + body?: any + headers?: HeadersInit + } + ): Promise => { const config = getConfig() let res: Response diff --git a/packages/bigcommerce/src/api/utils/get-customer-id.ts b/packages/bigcommerce/src/api/utils/get-customer-id.ts index 7344e017b..e66bb5d30 100644 --- a/packages/bigcommerce/src/api/utils/get-customer-id.ts +++ b/packages/bigcommerce/src/api/utils/get-customer-id.ts @@ -20,9 +20,7 @@ async function getCustomerId({ getCustomerIdQuery, undefined, { - headers: { - cookie: `${config.customerCookie}=${customerToken}`, - }, + 'Set-Cookie': `${config.customerCookie}=${customerToken}`, } ) diff --git a/packages/bigcommerce/src/auth/use-login.tsx b/packages/bigcommerce/src/auth/use-login.tsx index 103c4ccbf..2e7bade2e 100644 --- a/packages/bigcommerce/src/auth/use-login.tsx +++ b/packages/bigcommerce/src/auth/use-login.tsx @@ -9,7 +9,7 @@ export default useLogin as UseLogin export const handler: MutationHook = { fetchOptions: { - url: '/api/login', + url: '/api/commerce/login', method: 'POST', }, async fetcher({ input: { email, password }, options, fetch }) { diff --git a/packages/bigcommerce/src/auth/use-logout.tsx b/packages/bigcommerce/src/auth/use-logout.tsx index ff707abf1..a18f4ded9 100644 --- a/packages/bigcommerce/src/auth/use-logout.tsx +++ b/packages/bigcommerce/src/auth/use-logout.tsx @@ -8,7 +8,7 @@ export default useLogout as UseLogout export const handler: MutationHook = { fetchOptions: { - url: '/api/logout', + url: '/api/commerce/logout', method: 'GET', }, useHook: diff --git a/packages/bigcommerce/src/auth/use-signup.tsx b/packages/bigcommerce/src/auth/use-signup.tsx index 893b2beb2..c8f848907 100644 --- a/packages/bigcommerce/src/auth/use-signup.tsx +++ b/packages/bigcommerce/src/auth/use-signup.tsx @@ -9,7 +9,7 @@ export default useSignup as UseSignup export const handler: MutationHook = { fetchOptions: { - url: '/api/signup', + url: '/api/commerce/signup', method: 'POST', }, async fetcher({ diff --git a/packages/bigcommerce/src/cart/use-add-item.tsx b/packages/bigcommerce/src/cart/use-add-item.tsx index f65e82bc7..028bea621 100644 --- a/packages/bigcommerce/src/cart/use-add-item.tsx +++ b/packages/bigcommerce/src/cart/use-add-item.tsx @@ -9,7 +9,7 @@ export default useAddItem as UseAddItem export const handler: MutationHook = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'POST', }, async fetcher({ input: item, options, fetch }) { @@ -33,7 +33,6 @@ export const handler: MutationHook = { ({ fetch }) => () => { const { mutate } = useCart() - return useCallback( async function addItem(input) { const data = await fetch({ input }) diff --git a/packages/bigcommerce/src/cart/use-cart.tsx b/packages/bigcommerce/src/cart/use-cart.tsx index f8c4691bf..24c0a0764 100644 --- a/packages/bigcommerce/src/cart/use-cart.tsx +++ b/packages/bigcommerce/src/cart/use-cart.tsx @@ -7,7 +7,7 @@ export default useCart as UseCart export const handler: SWRHook = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'GET', }, useHook: diff --git a/packages/bigcommerce/src/cart/use-remove-item.tsx b/packages/bigcommerce/src/cart/use-remove-item.tsx index bed12c96b..74f893ccb 100644 --- a/packages/bigcommerce/src/cart/use-remove-item.tsx +++ b/packages/bigcommerce/src/cart/use-remove-item.tsx @@ -26,7 +26,7 @@ export default useRemoveItem as UseRemoveItem export const handler = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'DELETE', }, async fetcher({ diff --git a/packages/bigcommerce/src/cart/use-update-item.tsx b/packages/bigcommerce/src/cart/use-update-item.tsx index b15690ed7..f5bfbc410 100644 --- a/packages/bigcommerce/src/cart/use-update-item.tsx +++ b/packages/bigcommerce/src/cart/use-update-item.tsx @@ -5,7 +5,9 @@ import type { HookFetcherContext, } from '@vercel/commerce/utils/types' import { ValidationError } from '@vercel/commerce/utils/errors' -import useUpdateItem, { UseUpdateItem } from '@vercel/commerce/cart/use-update-item' +import useUpdateItem, { + UseUpdateItem, +} from '@vercel/commerce/cart/use-update-item' import type { LineItem, UpdateItemHook } from '@vercel/commerce/types/cart' import { handler as removeItemHandler } from './use-remove-item' import useCart from './use-cart' @@ -18,7 +20,7 @@ export default useUpdateItem as UseUpdateItem export const handler = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'PUT', }, async fetcher({ diff --git a/packages/bigcommerce/src/customer/use-customer.tsx b/packages/bigcommerce/src/customer/use-customer.tsx index e6b8f4968..182aee2cc 100644 --- a/packages/bigcommerce/src/customer/use-customer.tsx +++ b/packages/bigcommerce/src/customer/use-customer.tsx @@ -8,7 +8,7 @@ export default useCustomer as UseCustomer export const handler: SWRHook = { fetchOptions: { - url: '/api/customer', + url: '/api/commerce/customer', method: 'GET', }, async fetcher({ options, fetch }) { diff --git a/packages/bigcommerce/src/index.tsx b/packages/bigcommerce/src/index.tsx index a7de60924..eba1e4205 100644 --- a/packages/bigcommerce/src/index.tsx +++ b/packages/bigcommerce/src/index.tsx @@ -1,4 +1,7 @@ -import { getCommerceProvider, useCommerce as useCoreCommerce } from '@vercel/commerce' +import { + getCommerceProvider, + useCommerce as useCoreCommerce, +} from '@vercel/commerce' import { bigcommerceProvider, BigcommerceProvider } from './provider' export { bigcommerceProvider } diff --git a/packages/bigcommerce/src/lib/normalize.ts b/packages/bigcommerce/src/lib/normalize.ts index b01de3233..e83394c91 100644 --- a/packages/bigcommerce/src/lib/normalize.ts +++ b/packages/bigcommerce/src/lib/normalize.ts @@ -3,9 +3,9 @@ import type { Product } from '@vercel/commerce/types/product' import type { Cart, LineItem } from '@vercel/commerce/types/cart' import type { Category, Brand } from '@vercel/commerce/types/site' import type { BigcommerceCart, BCCategory, BCBrand } from '../types' +import type { ProductNode } from '../api/operations/get-all-products' +import type { definitions } from '../api/definitions/store-content' -import { definitions } from '../api/definitions/store-content' -import update from './immutability' import getSlug from './get-slug' function normalizeProductOption(productOption: any) { @@ -20,55 +20,44 @@ function normalizeProductOption(productOption: any) { } } -export function normalizeProduct(productNode: any): Product { +export function normalizeProduct(productNode: ProductNode): Product { const { entityId: id, productOptions, prices, path, - id: _, - options: _0, + images, + variants, } = productNode - return update(productNode, { - id: { $set: String(id) }, - images: { - $apply: ({ edges }: any) => - edges?.map(({ node: { urlOriginal, altText, ...rest } }: any) => ({ - url: urlOriginal, - alt: altText, - ...rest, - })), - }, - variants: { - $apply: ({ edges }: any) => - edges?.map(({ node: { entityId, productOptions, ...rest } }: any) => ({ + return { + id: String(id), + name: productNode.name, + description: productNode.description, + images: + images.edges?.map(({ node: { urlOriginal, altText, ...rest } }: any) => ({ + url: urlOriginal, + alt: altText, + ...rest, + })) || [], + path: `/${getSlug(path)}`, + variants: + variants.edges?.map( + ({ node: { entityId, productOptions, ...rest } }: any) => ({ id: String(entityId), options: productOptions?.edges ? productOptions.edges.map(normalizeProductOption) : [], ...rest, - })), - }, - options: { - $set: productOptions.edges - ? productOptions?.edges.map(normalizeProductOption) - : [], - }, - brand: { - $apply: (brand: any) => (brand?.id ? brand.id : null), - }, - slug: { - $set: path?.replace(/^\/+|\/+$/g, ''), - }, + }) + ) || [], + options: productOptions?.edges?.map(normalizeProductOption) || [], + slug: path?.replace(/^\/+|\/+$/g, ''), price: { - $set: { - value: prices?.price.value, - currencyCode: prices?.price.currencyCode, - }, + value: prices?.price.value, + currencyCode: prices?.price.currencyCode, }, - $unset: ['entityId'], - }) + } } export function normalizePage(page: definitions['page_Full']): Page { @@ -122,7 +111,7 @@ function normalizeLineItem(item: any): LineItem { listPrice: item.list_price, }, options: item.options, - path: item.url.split('/')[3], + path: `/${item.url.split('/')[3]}`, discounts: item.discounts.map((discount: any) => ({ value: discount.discounted_amount, })), diff --git a/packages/bigcommerce/src/product/use-search.tsx b/packages/bigcommerce/src/product/use-search.tsx index 9edd47f77..038fc9b7f 100644 --- a/packages/bigcommerce/src/product/use-search.tsx +++ b/packages/bigcommerce/src/product/use-search.tsx @@ -14,7 +14,7 @@ export type SearchProductsInput = { export const handler: SWRHook = { fetchOptions: { - url: '/api/catalog/products', + url: '/api/commerce/catalog/products', method: 'GET', }, fetcher({ input: { search, categoryId, brandId, sort }, options, fetch }) { diff --git a/packages/bigcommerce/src/wishlist/use-add-item.tsx b/packages/bigcommerce/src/wishlist/use-add-item.tsx index ba12eabf8..949955bd6 100644 --- a/packages/bigcommerce/src/wishlist/use-add-item.tsx +++ b/packages/bigcommerce/src/wishlist/use-add-item.tsx @@ -12,7 +12,7 @@ export default useAddItem as UseAddItem export const handler: MutationHook = { fetchOptions: { - url: '/api/wishlist', + url: '/api/commerce/wishlist', method: 'POST', }, useHook: diff --git a/packages/bigcommerce/src/wishlist/use-remove-item.tsx b/packages/bigcommerce/src/wishlist/use-remove-item.tsx index 63cca503d..c1b3e8784 100644 --- a/packages/bigcommerce/src/wishlist/use-remove-item.tsx +++ b/packages/bigcommerce/src/wishlist/use-remove-item.tsx @@ -12,7 +12,7 @@ export default useRemoveItem as UseRemoveItem export const handler: MutationHook = { fetchOptions: { - url: '/api/wishlist', + url: '/api/commerce/wishlist', method: 'DELETE', }, useHook: diff --git a/packages/bigcommerce/src/wishlist/use-wishlist.tsx b/packages/bigcommerce/src/wishlist/use-wishlist.tsx index 058b03c8c..89ae69c38 100644 --- a/packages/bigcommerce/src/wishlist/use-wishlist.tsx +++ b/packages/bigcommerce/src/wishlist/use-wishlist.tsx @@ -10,7 +10,7 @@ import type { GetWishlistHook } from '@vercel/commerce/types/wishlist' export default useWishlist as UseWishlist export const handler: SWRHook = { fetchOptions: { - url: '/api/wishlist', + url: '/api/commerce/wishlist', method: 'GET', }, async fetcher({ input: { customerId, includeProducts }, options, fetch }) { diff --git a/packages/commerce/new-provider.md b/packages/commerce/new-provider.md index c75076175..bd51f1461 100644 --- a/packages/commerce/new-provider.md +++ b/packages/commerce/new-provider.md @@ -69,7 +69,10 @@ Then, open [/site/.env.template](/site/.env.template) and add the provider name Using BigCommerce as an example. The first thing to do is export a `CommerceProvider` component that includes a `provider` object with all the handlers that can be used for hooks: ```tsx -import { getCommerceProvider, useCommerce as useCoreCommerce } from '@vercel/commerce' +import { + getCommerceProvider, + useCommerce as useCoreCommerce, +} from '@vercel/commerce' import { bigcommerceProvider, BigcommerceProvider } from './provider' export { bigcommerceProvider } @@ -135,7 +138,7 @@ export default useCart as UseCart export const handler: SWRHook = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'GET', }, useHook: @@ -175,7 +178,7 @@ export default useAddItem as UseAddItem export const handler: MutationHook = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'POST', }, async fetcher({ input: item, options, fetch }) { @@ -213,25 +216,26 @@ export const handler: MutationHook = { ``` ## Showing progress and features + When creating a PR for a new provider, include this list in the PR description and mark the progress as you push so we can organize the code review. Not all points are required (but advised) so make sure to keep the list up to date. **Status** -* [ ] CommerceProvider -* [ ] Schema & TS types -* [ ] API Operations - Get all collections -* [ ] API Operations - Get all pages -* [ ] API Operations - Get all products -* [ ] API Operations - Get page -* [ ] API Operations - Get product -* [ ] API Operations - Get Shop Info (categories and vendors working — `vendors` query still a WIP PR on Reaction) -* [ ] Hook - Add Item -* [ ] Hook - Remove Item -* [ ] Hook - Update Item -* [ ] Hook - Get Cart (account-tied carts working, anonymous carts working, cart reconciliation working) -* [ ] Auth (based on a WIP PR on Reaction - still need to implement refresh tokens) -* [ ] Customer information -* [ ] Product attributes - Size, Colors -* [ ] Custom checkout -* [ ] Typing (in progress) -* [ ] Tests +- [ ] CommerceProvider +- [ ] Schema & TS types +- [ ] API Operations - Get all collections +- [ ] API Operations - Get all pages +- [ ] API Operations - Get all products +- [ ] API Operations - Get page +- [ ] API Operations - Get product +- [ ] API Operations - Get Shop Info (categories and vendors working — `vendors` query still a WIP PR on Reaction) +- [ ] Hook - Add Item +- [ ] Hook - Remove Item +- [ ] Hook - Update Item +- [ ] Hook - Get Cart (account-tied carts working, anonymous carts working, cart reconciliation working) +- [ ] Auth (based on a WIP PR on Reaction - still need to implement refresh tokens) +- [ ] Customer information +- [ ] Product attributes - Size, Colors +- [ ] Custom checkout +- [ ] Typing (in progress) +- [ ] Tests diff --git a/packages/commerce/package.json b/packages/commerce/package.json index 784201f2e..0003c6824 100644 --- a/packages/commerce/package.json +++ b/packages/commerce/package.json @@ -47,13 +47,12 @@ } }, "dependencies": { - "@vercel/fetch": "^6.2.0", + "@vercel/edge": "^0.0.4", "deepmerge": "^4.2.2", "import-cwd": "^3.0.0", "js-cookie": "^3.0.1", "swr": "^1.3.0", - "node-fetch": "^2.6.7", - "zod": "^3.19.0" + "zod": "^3.19.1" }, "peerDependencies": { "next": "^12", @@ -66,8 +65,8 @@ "@taskr/watch": "^1.1.0", "@types/js-cookie": "^3.0.1", "@types/node": "^17.0.8", - "@types/react": "^18.0.14", "@types/node-fetch": "2.6.2", + "@types/react": "^18.0.14", "lint-staged": "^12.1.7", "next": "^12.0.8", "prettier": "^2.5.1", diff --git a/packages/commerce/src/api/endpoints/cart.ts b/packages/commerce/src/api/endpoints/cart.ts index e5a05dbd6..a1a33cc40 100644 --- a/packages/commerce/src/api/endpoints/cart.ts +++ b/packages/commerce/src/api/endpoints/cart.ts @@ -1,62 +1,64 @@ -import type { CartSchema } from '../../types/cart' -import { CommerceAPIError } from '../utils/errors' -import isAllowedOperation from '../utils/is-allowed-operation' import type { GetAPISchema } from '..' +import type { CartSchema } from '../../types/cart' + +import { parse, getInput } from '../utils' +import validateHandlers from '../utils/validate-handlers' + +import { + getCartBodySchema, + addItemBodySchema, + updateItemBodySchema, + removeItemBodySchema, + cartSchema, +} from '../../schemas/cart' const cartEndpoint: GetAPISchema< any, CartSchema >['endpoint']['handler'] = async (ctx) => { - const { req, res, handlers, config } = ctx + const { req, handlers, config } = ctx - if ( - !isAllowedOperation(req, res, { - GET: handlers['getCart'], - POST: handlers['addItem'], - PUT: handlers['updateItem'], - DELETE: handlers['removeItem'], - }) - ) { - return - } + validateHandlers(req, { + GET: handlers['getCart'], + POST: handlers['addItem'], + PUT: handlers['updateItem'], + DELETE: handlers['removeItem'], + }) + const input = await getInput(req) + + let output const { cookies } = req - const cartId = cookies[config.cartCookie] + const cartId = cookies.get(config.cartCookie) - try { - // Return current cart info - if (req.method === 'GET') { - const body = { cartId } - return await handlers['getCart']({ ...ctx, body }) - } - - // Create or add an item to the cart - if (req.method === 'POST') { - const body = { ...req.body, cartId } - return await handlers['addItem']({ ...ctx, body }) - } - - // Update item in cart - if (req.method === 'PUT') { - const body = { ...req.body, cartId } - return await handlers['updateItem']({ ...ctx, body }) - } - - // Remove an item from the cart - if (req.method === 'DELETE') { - const body = { ...req.body, cartId } - return await handlers['removeItem']({ ...ctx, body }) - } - } catch (error) { - console.error(error) - - const message = - error instanceof CommerceAPIError - ? 'An unexpected error ocurred with the Commerce API' - : 'An unexpected error ocurred' - - res.status(500).json({ data: null, errors: [{ message }] }) + // Return current cart info + if (req.method === 'GET') { + const body = getCartBodySchema.parse({ cartId }) + output = await handlers['getCart']({ ...ctx, body }) } + + // Create or add an item to the cart + if (req.method === 'POST') { + const body = addItemBodySchema.parse({ ...input, cartId }) + if (!body.item.quantity) { + body.item.quantity = 1 + } + output = await handlers['addItem']({ ...ctx, body }) + } + + // Update item in cart + if (req.method === 'PUT') { + const body = updateItemBodySchema.parse({ ...input, cartId }) + output = await handlers['updateItem']({ ...ctx, body }) + } + + // Remove an item from the cart + if (req.method === 'DELETE') { + const body = removeItemBodySchema.parse({ ...input, cartId }) + return await handlers['removeItem']({ ...ctx, body }) + } + + return output ? parse(output, cartSchema.nullish()) : { status: 405 } } export default cartEndpoint diff --git a/packages/commerce/src/api/endpoints/catalog/products.ts b/packages/commerce/src/api/endpoints/catalog/products.ts index d2a4794be..19d1723ee 100644 --- a/packages/commerce/src/api/endpoints/catalog/products.ts +++ b/packages/commerce/src/api/endpoints/catalog/products.ts @@ -1,31 +1,37 @@ -import type { ProductsSchema } from '../../../types/product' -import { CommerceAPIError } from '../../utils/errors' -import isAllowedOperation from '../../utils/is-allowed-operation' import type { GetAPISchema } from '../..' +import type { ProductsSchema } from '../../../types/product' + +import validateHandlers from '../../utils/validate-handlers' +import { + searchProductBodySchema, + searchProductsSchema, +} from '../../../schemas/product' +import { parse } from '../../utils' const productsEndpoint: GetAPISchema< any, ProductsSchema >['endpoint']['handler'] = async (ctx) => { - const { req, res, handlers } = ctx + const { req, handlers } = ctx - if (!isAllowedOperation(req, res, { GET: handlers['getProducts'] })) { - return + validateHandlers(req, { GET: handlers['getProducts'] }) + const { searchParams } = new URL(req.url) + + const body = searchProductBodySchema.parse({ + search: searchParams.get('search') ?? undefined, + categoryId: searchParams.get('categoryId') ?? undefined, + brandId: searchParams.get('brandId') ?? undefined, + sort: searchParams.get('sort') ?? undefined, + }) + + const res = await handlers['getProducts']({ ...ctx, body }) + + res.headers = { + 'Cache-Control': 'max-age=0, s-maxage=3600, stale-while-revalidate, public', + ...res.headers, } - try { - const body = req.query - return await handlers['getProducts']({ ...ctx, body }) - } catch (error) { - console.error(error) - - const message = - error instanceof CommerceAPIError - ? 'An unexpected error ocurred with the Commerce API' - : 'An unexpected error ocurred' - - res.status(500).json({ data: null, errors: [{ message }] }) - } + return parse(res, searchProductsSchema) } export default productsEndpoint diff --git a/packages/commerce/src/api/endpoints/checkout.ts b/packages/commerce/src/api/endpoints/checkout.ts index 0168e7065..58542bdf2 100644 --- a/packages/commerce/src/api/endpoints/checkout.ts +++ b/packages/commerce/src/api/endpoints/checkout.ts @@ -1,49 +1,45 @@ -import type { CheckoutSchema } from '../../types/checkout' import type { GetAPISchema } from '..' +import type { CheckoutSchema } from '../../types/checkout' -import { CommerceAPIError } from '../utils/errors' -import isAllowedOperation from '../utils/is-allowed-operation' +import { + checkoutSchema, + getCheckoutBodySchema, + submitCheckoutBodySchema, +} from '../../schemas/checkout' + +import { parse, getInput } from '../utils' +import validateHandlers from '../utils/validate-handlers' const checkoutEndpoint: GetAPISchema< any, CheckoutSchema >['endpoint']['handler'] = async (ctx) => { - const { req, res, handlers, config } = ctx + const { req, handlers, config } = ctx - if ( - !isAllowedOperation(req, res, { - GET: handlers['getCheckout'], - POST: handlers['submitCheckout'], - }) - ) { - return - } + validateHandlers(req, { + GET: handlers['getCheckout'], + POST: handlers['submitCheckout'], + }) const { cookies } = req - const cartId = cookies[config.cartCookie] + const cartId = cookies.get(config.cartCookie)! + const input = await getInput(req) - try { - // Create checkout - if (req.method === 'GET') { - const body = { ...req.body, cartId } - return await handlers['getCheckout']({ ...ctx, body }) - } - - // Create checkout - if (req.method === 'POST' && handlers['submitCheckout']) { - const body = { ...req.body, cartId } - return await handlers['submitCheckout']({ ...ctx, body }) - } - } catch (error) { - console.error(error) - - const message = - error instanceof CommerceAPIError - ? 'An unexpected error ocurred with the Commerce API' - : 'An unexpected error ocurred' - - res.status(500).json({ data: null, errors: [{ message }] }) + // Get checkout + if (req.method === 'GET') { + const body = getCheckoutBodySchema.parse({ ...input, cartId }) + const res = await handlers['getCheckout']({ ...ctx, body }) + return parse(res, checkoutSchema.optional()) } + + // Create checkout + if (req.method === 'POST' && handlers['submitCheckout']) { + const body = submitCheckoutBodySchema.parse({ ...input, cartId }) + const res = await handlers['submitCheckout']({ ...ctx, body }) + return parse(res, checkoutSchema.optional()) + } + + return { status: 405 } } export default checkoutEndpoint diff --git a/packages/commerce/src/api/endpoints/customer/address.ts b/packages/commerce/src/api/endpoints/customer/address.ts index d5ede697a..725c636ba 100644 --- a/packages/commerce/src/api/endpoints/customer/address.ts +++ b/packages/commerce/src/api/endpoints/customer/address.ts @@ -1,65 +1,68 @@ import type { CustomerAddressSchema } from '../../../types/customer/address' import type { GetAPISchema } from '../..' -import { CommerceAPIError } from '../../utils/errors' -import isAllowedOperation from '../../utils/is-allowed-operation' +import validateHandlers from '../../utils/validate-handlers' + +import { + addAddressBodySchema, + addressSchema, + deleteAddressBodySchema, + updateAddressBodySchema, +} from '../../../schemas/customer' + +import { parse, getInput } from '../../utils' +import { getCartBodySchema } from '../../../schemas/cart' + +// create a function that returns a function const customerShippingEndpoint: GetAPISchema< any, CustomerAddressSchema >['endpoint']['handler'] = async (ctx) => { - const { req, res, handlers, config } = ctx + const { req, handlers, config } = ctx - if ( - !isAllowedOperation(req, res, { - GET: handlers['getAddresses'], - POST: handlers['addItem'], - PUT: handlers['updateItem'], - DELETE: handlers['removeItem'], - }) - ) { - return - } + validateHandlers(req, { + GET: handlers['getAddresses'], + POST: handlers['addItem'], + PUT: handlers['updateItem'], + DELETE: handlers['removeItem'], + }) + let output + const input = await getInput(req) const { cookies } = req // Cart id might be usefull for anonymous shopping - const cartId = cookies[config.cartCookie] + const cartId = cookies.get(config.cartCookie) - try { - // Return customer addresses - if (req.method === 'GET') { - const body = { cartId } - return await handlers['getAddresses']({ ...ctx, body }) - } - - // Create or add an item to customer addresses list - if (req.method === 'POST') { - const body = { ...req.body, cartId } - return await handlers['addItem']({ ...ctx, body }) - } - - // Update item in customer addresses list - if (req.method === 'PUT') { - const body = { ...req.body, cartId } - return await handlers['updateItem']({ ...ctx, body }) - } - - // Remove an item from customer addresses list - if (req.method === 'DELETE') { - const body = { ...req.body, cartId } - return await handlers['removeItem']({ ...ctx, body }) - } - } catch (error) { - console.error(error) - - const message = - error instanceof CommerceAPIError - ? 'An unexpected error ocurred with the Commerce API' - : 'An unexpected error ocurred' - - res.status(500).json({ data: null, errors: [{ message }] }) + // Return customer addresses + if (req.method === 'GET') { + const body = getCartBodySchema.parse({ cartId }) + return parse( + await handlers['getAddresses']({ ...ctx, body }), + addressSchema + ) } + + // Create or add an item to customer addresses list + if (req.method === 'POST') { + const body = addAddressBodySchema.parse({ ...input, cartId }) + output = await handlers['addItem']({ ...ctx, body }) + } + + // Update item in customer addresses list + if (req.method === 'PUT') { + const body = updateAddressBodySchema.parse({ ...input, cartId }) + output = await handlers['updateItem']({ ...ctx, body }) + } + + // Remove an item from customer addresses list + if (req.method === 'DELETE') { + const body = deleteAddressBodySchema.parse({ ...input, cartId }) + return await handlers['removeItem']({ ...ctx, body }) + } + + return output ? parse(output, addressSchema) : { status: 405 } } export default customerShippingEndpoint diff --git a/packages/commerce/src/api/endpoints/customer/card.ts b/packages/commerce/src/api/endpoints/customer/card.ts index ad268cbb9..dbac43151 100644 --- a/packages/commerce/src/api/endpoints/customer/card.ts +++ b/packages/commerce/src/api/endpoints/customer/card.ts @@ -1,65 +1,67 @@ import type { CustomerCardSchema } from '../../../types/customer/card' import type { GetAPISchema } from '../..' -import { CommerceAPIError } from '../../utils/errors' -import isAllowedOperation from '../../utils/is-allowed-operation' +import { z } from 'zod' + +import { + cardSchema, + addCardBodySchema, + deleteCardBodySchema, + updateCardBodySchema, +} from '../../../schemas/customer' +import { parse, getInput } from '../../utils' + +import validateHandlers from '../../utils/validate-handlers' const customerCardEndpoint: GetAPISchema< any, CustomerCardSchema >['endpoint']['handler'] = async (ctx) => { - const { req, res, handlers, config } = ctx + const { req, handlers, config } = ctx - if ( - !isAllowedOperation(req, res, { - GET: handlers['getCards'], - POST: handlers['addItem'], - PUT: handlers['updateItem'], - DELETE: handlers['removeItem'], - }) - ) { - return - } + validateHandlers(req, { + GET: handlers['getCards'], + POST: handlers['addItem'], + PUT: handlers['updateItem'], + DELETE: handlers['removeItem'], + }) + let output + const input = await getInput(req) const { cookies } = req // Cart id might be usefull for anonymous shopping - const cartId = cookies[config.cartCookie] + const cartId = cookies.get(config.cartCookie) - try { - // Create or add a card - if (req.method === 'GET') { - const body = { ...req.body } - return await handlers['getCards']({ ...ctx, body }) - } - - // Create or add an item to customer cards - if (req.method === 'POST') { - const body = { ...req.body, cartId } - return await handlers['addItem']({ ...ctx, body }) - } - - // Update item in customer cards - if (req.method === 'PUT') { - const body = { ...req.body, cartId } - return await handlers['updateItem']({ ...ctx, body }) - } - - // Remove an item from customer cards - if (req.method === 'DELETE') { - const body = { ...req.body, cartId } - return await handlers['removeItem']({ ...ctx, body }) - } - } catch (error) { - console.error(error) - - const message = - error instanceof CommerceAPIError - ? 'An unexpected error ocurred with the Commerce API' - : 'An unexpected error ocurred' - - res.status(500).json({ data: null, errors: [{ message }] }) + // Create or add a card + if (req.method === 'GET') { + const body = { ...input } + return parse( + await handlers['getCards']({ ...ctx, body }), + z.array(cardSchema).optional() + ) } + + // Create or add an item to customer cards + if (req.method === 'POST') { + const body = addCardBodySchema.parse({ ...input, cartId }) + output = await handlers['addItem']({ ...ctx, body }) + } + + // Update item in customer cards + if (req.method === 'PUT') { + const body = updateCardBodySchema.parse({ ...input, cartId }) + output = await handlers['updateItem']({ ...ctx, body }) + } + + // Remove an item from customer cards + if (req.method === 'DELETE') { + const body = deleteCardBodySchema.parse({ ...input, cartId }) + + return await handlers['removeItem']({ ...ctx, body }) + } + + return output ? parse(output, cardSchema.nullish()) : { status: 405 } } export default customerCardEndpoint diff --git a/packages/commerce/src/api/endpoints/customer/index.ts b/packages/commerce/src/api/endpoints/customer/index.ts index 2107ebb89..92872c840 100644 --- a/packages/commerce/src/api/endpoints/customer/index.ts +++ b/packages/commerce/src/api/endpoints/customer/index.ts @@ -1,36 +1,25 @@ import type { CustomerSchema } from '../../../types/customer' import type { GetAPISchema } from '../..' -import { CommerceAPIError } from '../../utils/errors' -import isAllowedOperation from '../../utils/is-allowed-operation' +import { parse } from '../../utils' +import validateHandlers from '../../utils/validate-handlers' + +import { customerSchema } from '../../../schemas/customer' const customerEndpoint: GetAPISchema< any, CustomerSchema >['endpoint']['handler'] = async (ctx) => { - const { req, res, handlers } = ctx + const { req, handlers } = ctx - if ( - !isAllowedOperation(req, res, { - GET: handlers['getLoggedInCustomer'], - }) - ) { - return - } + validateHandlers(req, { + GET: handlers['getLoggedInCustomer'], + }) - try { - const body = null - return await handlers['getLoggedInCustomer']({ ...ctx, body }) - } catch (error) { - console.error(error) + const body = null + const output = await handlers['getLoggedInCustomer']({ ...ctx, body }) - const message = - error instanceof CommerceAPIError - ? 'An unexpected error ocurred with the Commerce API' - : 'An unexpected error ocurred' - - res.status(500).json({ data: null, errors: [{ message }] }) - } + return output ? parse(output, customerSchema) : { status: 204 } } export default customerEndpoint diff --git a/packages/commerce/src/api/endpoints/index.ts b/packages/commerce/src/api/endpoints/index.ts index e69de29bb..d5ed49ce9 100644 --- a/packages/commerce/src/api/endpoints/index.ts +++ b/packages/commerce/src/api/endpoints/index.ts @@ -0,0 +1,10 @@ +import edgeHandler from '../utils/edge-handler' +import nodeHandler from '../utils/node-handler' + +/** + * Next.js Commerce API endpoints handler. Based on the path, it will call the corresponding endpoint handler, + * exported from the `endpoints` folder of the provider. + * @param {CommerceAPI} commerce The Commerce API instance. + * @param endpoints An object containing the handlers for each endpoint. + */ +export default process.env.NEXT_RUNTIME === 'edge' ? edgeHandler : nodeHandler diff --git a/packages/commerce/src/api/endpoints/login.ts b/packages/commerce/src/api/endpoints/login.ts index cfec82e6a..565d0ab83 100644 --- a/packages/commerce/src/api/endpoints/login.ts +++ b/packages/commerce/src/api/endpoints/login.ts @@ -1,36 +1,25 @@ -import type { LoginSchema } from '../../types/login' -import { CommerceAPIError } from '../utils/errors' -import isAllowedOperation from '../utils/is-allowed-operation' import type { GetAPISchema } from '..' +import type { LoginSchema } from '../../types/login' + +import { getInput } from '../utils' +import validateHandlers from '../utils/validate-handlers' + +import { loginBodySchema } from '../../schemas/auth' const loginEndpoint: GetAPISchema< any, LoginSchema >['endpoint']['handler'] = async (ctx) => { - const { req, res, handlers } = ctx + const { req, handlers } = ctx - if ( - !isAllowedOperation(req, res, { - POST: handlers['login'], - GET: handlers['login'], - }) - ) { - return - } + validateHandlers(req, { + POST: handlers['login'], + GET: handlers['login'], + }) - try { - const body = req.body ?? {} - return await handlers['login']({ ...ctx, body }) - } catch (error) { - console.error(error) - - const message = - error instanceof CommerceAPIError - ? 'An unexpected error ocurred with the Commerce API' - : 'An unexpected error ocurred' - - res.status(500).json({ data: null, errors: [{ message }] }) - } + const input = await getInput(req) + const body = loginBodySchema.parse(input) + return handlers['login']({ ...ctx, body }) } export default loginEndpoint diff --git a/packages/commerce/src/api/endpoints/logout.ts b/packages/commerce/src/api/endpoints/logout.ts index c2640eac3..9f9fe23de 100644 --- a/packages/commerce/src/api/endpoints/logout.ts +++ b/packages/commerce/src/api/endpoints/logout.ts @@ -1,35 +1,26 @@ -import type { LogoutSchema } from '../../types/logout' -import { CommerceAPIError } from '../utils/errors' -import isAllowedOperation from '../utils/is-allowed-operation' import type { GetAPISchema } from '..' +import type { LogoutSchema } from '../../types/logout' -const logoutEndpoint: GetAPISchema['endpoint']['handler'] = - async (ctx) => { - const { req, res, handlers } = ctx +import { logoutBodySchema } from '../../schemas/auth' +import validateHandlers from '../utils/validate-handlers' - if ( - !isAllowedOperation(req, res, { - GET: handlers['logout'], - }) - ) { - return - } +const logoutEndpoint: GetAPISchema< + any, + LogoutSchema +>['endpoint']['handler'] = async (ctx) => { + const { req, handlers } = ctx - try { - const redirectTo = req.query.redirect_to - const body = typeof redirectTo === 'string' ? { redirectTo } : {} + validateHandlers(req, { + GET: handlers['logout'], + }) - return await handlers['logout']({ ...ctx, body }) - } catch (error) { - console.error(error) + const redirectTo = new URL(req.url).searchParams.get('redirectTo') - const message = - error instanceof CommerceAPIError - ? 'An unexpected error ocurred with the Commerce API' - : 'An unexpected error ocurred' + const body = logoutBodySchema.parse( + typeof redirectTo === 'string' ? { redirectTo } : {} + ) - res.status(500).json({ data: null, errors: [{ message }] }) - } - } + return handlers['logout']({ ...ctx, body }) +} export default logoutEndpoint diff --git a/packages/commerce/src/api/endpoints/signup.ts b/packages/commerce/src/api/endpoints/signup.ts index 78c4cf588..de25e7dc8 100644 --- a/packages/commerce/src/api/endpoints/signup.ts +++ b/packages/commerce/src/api/endpoints/signup.ts @@ -1,36 +1,27 @@ -import type { SignupSchema } from '../../types/signup' -import { CommerceAPIError } from '../utils/errors' -import isAllowedOperation from '../utils/is-allowed-operation' import type { GetAPISchema } from '..' +import type { SignupSchema } from '../../types/signup' -const signupEndpoint: GetAPISchema['endpoint']['handler'] = - async (ctx) => { - const { req, res, handlers, config } = ctx +import { getInput } from '../utils' +import validateHandlers from '../utils/validate-handlers' - if ( - !isAllowedOperation(req, res, { - POST: handlers['signup'], - }) - ) { - return - } +import { signupBodySchema } from '../../schemas/auth' - const { cookies } = req - const cartId = cookies[config.cartCookie] +const signupEndpoint: GetAPISchema< + any, + SignupSchema +>['endpoint']['handler'] = async (ctx) => { + const { req, handlers, config } = ctx - try { - const body = { ...req.body, cartId } - return await handlers['signup']({ ...ctx, body }) - } catch (error) { - console.error(error) + validateHandlers(req, { + POST: handlers['signup'], + }) - const message = - error instanceof CommerceAPIError - ? 'An unexpected error ocurred with the Commerce API' - : 'An unexpected error ocurred' + const input = await getInput(req) + const { cookies } = req + const cartId = cookies.get(config.cartCookie) - res.status(500).json({ data: null, errors: [{ message }] }) - } - } + const body = signupBodySchema.parse({ ...input, cartId }) + return handlers['signup']({ ...ctx, body }) +} export default signupEndpoint diff --git a/packages/commerce/src/api/endpoints/wishlist.ts b/packages/commerce/src/api/endpoints/wishlist.ts index 688fcd5da..6eec02ced 100644 --- a/packages/commerce/src/api/endpoints/wishlist.ts +++ b/packages/commerce/src/api/endpoints/wishlist.ts @@ -1,58 +1,58 @@ -import type { WishlistSchema } from '../../types/wishlist' -import { CommerceAPIError } from '../utils/errors' -import isAllowedOperation from '../utils/is-allowed-operation' import type { GetAPISchema } from '..' +import type { WishlistSchema } from '../../types/wishlist' + +import { parse, getInput } from '../utils' + +import { + wishlistSchema, + addItemBodySchema, + removeItemBodySchema, + getWishlistBodySchema, +} from '../../schemas/whishlist' + +import validateHandlers from '../utils/validate-handlers' const wishlistEndpoint: GetAPISchema< any, WishlistSchema >['endpoint']['handler'] = async (ctx) => { - const { req, res, handlers, config } = ctx + const { req, handlers, config } = ctx - if ( - !isAllowedOperation(req, res, { - GET: handlers['getWishlist'], - POST: handlers['addItem'], - DELETE: handlers['removeItem'], - }) - ) { - return - } + validateHandlers(req, { + GET: handlers['getWishlist'], + POST: handlers['addItem'], + DELETE: handlers['removeItem'], + }) + let output const { cookies } = req - const customerToken = cookies[config.customerCookie] + const input = await getInput(req) - try { - // Return current wishlist info - if (req.method === 'GET') { - const body = { - customerToken, - includeProducts: req.query.products === '1', - } - return await handlers['getWishlist']({ ...ctx, body }) - } + const customerToken = cookies.get(config.customerCookie) + const products = new URL(req.url).searchParams.get('products') - // Add an item to the wishlist - if (req.method === 'POST') { - const body = { ...req.body, customerToken } - return await handlers['addItem']({ ...ctx, body }) - } - - // Remove an item from the wishlist - if (req.method === 'DELETE') { - const body = { ...req.body, customerToken } - return await handlers['removeItem']({ ...ctx, body }) - } - } catch (error) { - console.error(error) - - const message = - error instanceof CommerceAPIError - ? 'An unexpected error ocurred with the Commerce API' - : 'An unexpected error ocurred' - - res.status(500).json({ data: null, errors: [{ message }] }) + // Return current wishlist info + if (req.method === 'GET') { + const body = getWishlistBodySchema.parse({ + customerToken, + includeProducts: !!products, + }) + output = await handlers['getWishlist']({ ...ctx, body }) } + + // Add an item to the wishlist + if (req.method === 'POST') { + const body = addItemBodySchema.parse({ ...input, customerToken }) + output = await handlers['addItem']({ ...ctx, body }) + } + + // Remove an item from the wishlist + if (req.method === 'DELETE') { + const body = removeItemBodySchema.parse({ ...input, customerToken }) + output = await handlers['removeItem']({ ...ctx, body }) + } + + return output ? parse(output, wishlistSchema.optional()) : { status: 405 } } export default wishlistEndpoint diff --git a/packages/commerce/src/api/index.ts b/packages/commerce/src/api/index.ts index 4e10e3666..7cd52eb94 100644 --- a/packages/commerce/src/api/index.ts +++ b/packages/commerce/src/api/index.ts @@ -1,6 +1,5 @@ -import type { NextApiHandler } from 'next' -import type { FetchOptions, Response } from '@vercel/fetch' -import type { APIEndpoint, APIHandler } from './utils/types' +import type { NextRequest } from 'next/server' +import type { APIEndpoint, APIHandler, APIResponse } from './utils/types' import type { CartSchema } from '../types/cart' import type { CustomerSchema } from '../types/customer' import type { LoginSchema } from '../types/login' @@ -119,6 +118,8 @@ export function getCommerceApi

( return commerce } +export type EndpointHandler = (req: NextRequest) => Promise + export function getEndpoint< P extends APIProvider, T extends GetAPISchema @@ -128,13 +129,11 @@ export function getEndpoint< config?: P['config'] options?: T['schema']['endpoint']['options'] } -): NextApiHandler { +): EndpointHandler { const cfg = commerce.getConfig(context.config) - - return function apiHandler(req, res) { + return function apiHandler(req) { return context.handler({ req, - res, commerce, config: cfg, handlers: context.handlers, @@ -151,7 +150,7 @@ export const createEndpoint = config?: P['config'] options?: API['schema']['endpoint']['options'] } - ): NextApiHandler => { + ): EndpointHandler => { return getEndpoint(commerce, { ...endpoint, ...context }) } @@ -166,7 +165,7 @@ export interface CommerceAPIConfig { fetch( query: string, queryData?: CommerceAPIFetchOptions, - fetchOptions?: FetchOptions + headers?: HeadersInit ): Promise> } @@ -175,8 +174,7 @@ export type GraphQLFetcher< Variables = any > = ( query: string, - queryData?: CommerceAPIFetchOptions, - fetchOptions?: FetchOptions + queryData?: CommerceAPIFetchOptions ) => Promise export interface GraphQLFetcherResult { diff --git a/packages/commerce/src/api/operations.ts b/packages/commerce/src/api/operations.ts index daf66acd4..7d4350f4d 100644 --- a/packages/commerce/src/api/operations.ts +++ b/packages/commerce/src/api/operations.ts @@ -1,4 +1,3 @@ -import type { ServerResponse } from 'http' import type { LoginOperation } from '../types/login' import type { GetAllPagesOperation, GetPageOperation } from '../types/page' import type { GetSiteInfoOperation } from '../types/site' @@ -44,14 +43,14 @@ export type Operations

= { (opts: { variables: T['variables'] config?: P['config'] - res: ServerResponse + res: Response }): Promise ( opts: { variables: T['variables'] config?: P['config'] - res: ServerResponse + res: Response } & OperationOptions ): Promise } diff --git a/packages/commerce/src/api/utils/edge-handler.ts b/packages/commerce/src/api/utils/edge-handler.ts new file mode 100644 index 000000000..c438bd9fc --- /dev/null +++ b/packages/commerce/src/api/utils/edge-handler.ts @@ -0,0 +1,81 @@ +import type { APIProvider, CommerceAPI, EndpointHandler } from '..' +import type { NextRequest } from 'next/server' + +import { normalizeApiError } from './errors' +import { transformHeaders } from '.' + +export default function edgeHandler

( + commerce: CommerceAPI

, + endpoints: Record) => EndpointHandler> +) { + const endpointsKeys = Object.keys(endpoints) + + const handlers = endpointsKeys.reduce>( + (acc, endpoint) => + Object.assign(acc, { + [endpoint]: endpoints[endpoint](commerce), + }), + {} + ) + + return async (req: NextRequest) => { + try { + const { pathname } = new URL(req.url) + + /** + * Get the current endpoint by removing the leading and trailing slash & base path. + * Csovers: /api/commerce/cart & /checkout + */ + const endpoint = pathname + .replace('/api/commerce/', '') + .replace(/^\/|\/$/g, '') + + // Check if the handler for this path exists and return a 404 if it doesn't + if (!endpointsKeys.includes(endpoint)) { + throw new Error( + `Endpoint "${endpoint}" not implemented. Please use one of the available api endpoints: ${endpointsKeys.join( + ', ' + )}` + ) + } + + /** + * Executes the handler for this endpoint, provided by the provider, + * parses the input body and returns the parsed output + */ + const output = await handlers[endpoint](req) + + // If the output is a Response, return it directly (E.g. checkout page & validateMethod util) + if (output instanceof Response) { + return output + } + + const headers = transformHeaders(output.headers) + + // If the output contains a redirectTo property, return a Response with the redirect + if (output.redirectTo) { + headers.append('Location', output.redirectTo) + return new Response(null, { + status: 302, + headers, + }) + } + + // Otherwise, return a JSON response with the output data or errors returned by the handler + const { data = null, errors, status } = output + return new Response(JSON.stringify({ data, errors }), { + status, + headers, + }) + } catch (error) { + const output = normalizeApiError(error) + if (output instanceof Response) { + return output + } + const { status = 500, ...rest } = output + return output instanceof Response + ? output + : new Response(JSON.stringify(rest), { status }) + } + } +} diff --git a/packages/commerce/src/api/utils/errors.ts b/packages/commerce/src/api/utils/errors.ts index 065faf34a..5d54f8a75 100644 --- a/packages/commerce/src/api/utils/errors.ts +++ b/packages/commerce/src/api/utils/errors.ts @@ -1,9 +1,9 @@ +import type { NextRequest } from 'next/server' + +import { CommerceError } from '../../utils/errors' import { ZodError } from 'zod' -import type { Response } from '@vercel/fetch' -import { CommerceError } from '../../utils/errors' - -export class CommerceAPIError extends Error { +export class CommerceAPIResponseError extends Error { status: number res: Response data: any @@ -17,6 +17,23 @@ export class CommerceAPIError extends Error { } } +export class CommerceAPIError extends Error { + status: number + code: string + constructor( + msg: string, + options?: { + status?: number + code?: string + } + ) { + super(msg) + this.name = 'CommerceApiError' + this.status = options?.status || 500 + this.code = options?.code || 'api_error' + } +} + export class CommerceNetworkError extends Error { constructor(msg: string) { super(msg) @@ -24,24 +41,59 @@ export class CommerceNetworkError extends Error { } } +export const normalizeZodIssues = (issues: ZodError['issues']) => + issues.map(({ path, message }) => `${message} at "${path.join('.')}" field`) + export const getOperationError = (operation: string, error: unknown) => { if (error instanceof ZodError) { return new CommerceError({ code: 'SCHEMA_VALIDATION_ERROR', message: - `The ${operation} operation returned invalid data and has ${ - error.issues.length - } parse ${error.issues.length === 1 ? 'error' : 'errors'}: \n` + - error.issues - .map( - (e, index) => - `Error #${index + 1} ${ - e.path.length > 0 ? `Path: ${e.path.join('.')}, ` : '' - }Code: ${e.code}, Message: ${e.message}` - ) - .join('\n'), + `Validation ${ + error.issues.length === 1 ? 'error' : 'errors' + } at "${operation}" operation: \n` + + normalizeZodIssues(error.issues).join('\n'), }) } - return error } + +export const normalizeApiError = (error: unknown, req?: NextRequest) => { + if (error instanceof CommerceAPIResponseError && error.res) { + return error.res + } + + req?.url && console.log(req.url) + + if (error instanceof ZodError) { + const message = 'Validation error, please check the input data!' + const errors = normalizeZodIssues(error.issues).map((message) => ({ + message, + })) + console.error(`${message}\n${errors.map((e) => e.message).join('\n')}`) + return { + status: 400, + data: null, + errors, + } + } + + console.error(error) + + if (error instanceof CommerceAPIError) { + return { + errors: [ + { + message: error.message, + code: error.code, + }, + ], + status: error.status, + } + } + + return { + data: null, + errors: [{ message: 'An unexpected error ocurred' }], + } +} diff --git a/packages/commerce/src/api/utils/index.ts b/packages/commerce/src/api/utils/index.ts new file mode 100644 index 000000000..3fd50d00e --- /dev/null +++ b/packages/commerce/src/api/utils/index.ts @@ -0,0 +1,88 @@ +import type { NextApiRequest, NextApiResponse } from 'next' +import type { ZodSchema } from 'zod' +import type { APIResponse } from './types' + +import { NextRequest } from 'next/server' + +/** + * Parses the output data of the API handler and returns a valid APIResponse + * or throws an error if the data is invalid. + * @param res APIResponse + * @param parser ZodSchema + */ +export const parse = (res: APIResponse, parser: ZodSchema) => { + if (res.data) { + res.data = parser.parse(res.data) + } + return res +} + +/** + * Returns the body of the request as a JSON object. + * @param req NextRequest + */ +export const getInput = (req: NextRequest) => req.json().catch(() => ({})) + +/** + * Convert NextApiRequest to NextRequest + * @param req NextApiRequest + * @param path string + */ +export const transformRequest = (req: NextApiRequest, path: string) => { + const headers = new Headers() + let body + + for (let i = 0; i < req.rawHeaders.length; i += 2) { + headers.append(req.rawHeaders[i], req.rawHeaders[i + 1]) + } + + if ( + req.method === 'POST' || + req.method === 'PUT' || + req.method === 'DELETE' + ) { + body = JSON.stringify(req.body) + } + + return new NextRequest(`https://${req.headers.host}/api/commerce/${path}`, { + headers, + method: req.method, + body, + }) +} + +/** + * Sets the custom headers received in the APIResponse in the + * @param headers Record | Headers | undefined + * @returns Headers + */ +export const transformHeaders = ( + headers: Record | Headers = {} +) => { + if (headers instanceof Headers) { + return headers + } + + const newHeaders = new Headers() + + Object.entries(headers).forEach(([key, value]) => { + newHeaders.append(key, value as string) + }) + + return newHeaders +} + +export const setHeaders = ( + res: NextApiResponse, + headers: Record | Headers = {} +) => { + if (headers instanceof Headers) { + headers.forEach((value, key) => { + res.setHeader(key, value) + }) + } else { + Object.entries(headers).forEach(([key, value]) => { + res.setHeader(key, value) + }) + } +} diff --git a/packages/commerce/src/api/utils/is-allowed-method.ts b/packages/commerce/src/api/utils/is-allowed-method.ts deleted file mode 100644 index 51c37e221..000000000 --- a/packages/commerce/src/api/utils/is-allowed-method.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next' - -export type HTTP_METHODS = 'OPTIONS' | 'GET' | 'POST' | 'PUT' | 'DELETE' - -export default function isAllowedMethod( - req: NextApiRequest, - res: NextApiResponse, - allowedMethods: HTTP_METHODS[] -) { - const methods = allowedMethods.includes('OPTIONS') - ? allowedMethods - : [...allowedMethods, 'OPTIONS'] - - if (!req.method || !methods.includes(req.method)) { - res.status(405) - res.setHeader('Allow', methods.join(', ')) - res.end() - return false - } - - if (req.method === 'OPTIONS') { - res.status(200) - res.setHeader('Allow', methods.join(', ')) - res.setHeader('Content-Length', '0') - res.end() - return false - } - - return true -} diff --git a/packages/commerce/src/api/utils/is-allowed-operation.ts b/packages/commerce/src/api/utils/is-allowed-operation.ts deleted file mode 100644 index f507781bf..000000000 --- a/packages/commerce/src/api/utils/is-allowed-operation.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next' -import isAllowedMethod, { HTTP_METHODS } from './is-allowed-method' -import { APIHandler } from './types' - -export default function isAllowedOperation( - req: NextApiRequest, - res: NextApiResponse, - allowedOperations: { [k in HTTP_METHODS]?: APIHandler } -) { - const methods = Object.keys(allowedOperations) as HTTP_METHODS[] - const allowedMethods = methods.reduce((arr, method) => { - if (allowedOperations[method]) { - arr.push(method) - } - return arr - }, []) - - return isAllowedMethod(req, res, allowedMethods) -} diff --git a/packages/commerce/src/api/utils/node-handler.ts b/packages/commerce/src/api/utils/node-handler.ts new file mode 100644 index 000000000..b08af9b0a --- /dev/null +++ b/packages/commerce/src/api/utils/node-handler.ts @@ -0,0 +1,75 @@ +import type { NextApiRequest, NextApiResponse } from 'next' +import type { APIProvider, CommerceAPI, EndpointHandler } from '..' + +import { normalizeApiError } from './errors' +import { transformRequest, setHeaders } from '.' + +export default function nodeHandler

( + commerce: CommerceAPI

, + endpoints: { + [key: string]: (commerce: CommerceAPI

) => EndpointHandler + } +) { + const paths = Object.keys(endpoints) + + const handlers = paths.reduce>( + (acc, path) => + Object.assign(acc, { + [path]: endpoints[path](commerce), + }), + {} + ) + + return async (req: NextApiRequest, res: NextApiResponse) => { + try { + if (!req.query.commerce) { + throw new Error( + 'Invalid configuration. Please make sure that the /pages/api/commerce/[[...commerce]].ts route is configured correctly, and it passes the commerce instance.' + ) + } + + /** + * Get the url path + */ + const path = Array.isArray(req.query.commerce) + ? req.query.commerce.join('/') + : req.query.commerce + + // Check if the handler for this path exists and return a 404 if it doesn't + if (!paths.includes(path)) { + throw new Error( + `Endpoint handler not implemented. Please use one of the available api endpoints: ${paths.join( + ', ' + )}` + ) + } + + const output = await handlers[path](transformRequest(req, path)) + const { status, errors, data, redirectTo, headers } = output + + setHeaders(res, headers) + + if (output instanceof Response) { + return res.end(output.body) + } + + if (redirectTo) { + return res.redirect(redirectTo) + } + + res.status(status || 200).json({ + data, + errors, + }) + } catch (error) { + const output = normalizeApiError(error) + + if (output instanceof Response) { + return res.end(output.body) + } + + const { status = 500, ...rest } = output + res.status(status).json(rest) + } + } +} diff --git a/packages/commerce/src/api/utils/types.ts b/packages/commerce/src/api/utils/types.ts index 27a95df40..f048c60c1 100644 --- a/packages/commerce/src/api/utils/types.ts +++ b/packages/commerce/src/api/utils/types.ts @@ -1,14 +1,19 @@ -import type { NextApiRequest, NextApiResponse } from 'next' +import type { NextRequest } from 'next/server' import type { CommerceAPI } from '..' export type ErrorData = { message: string; code?: string } -export type APIResponse = - | { data: Data; errors?: ErrorData[] } - // If `data` doesn't include `null`, then `null` is only allowed on errors - | (Data extends null - ? { data: null; errors?: ErrorData[] } - : { data: null; errors: ErrorData[] }) +export type APIResponse = { + data?: Data + errors?: ErrorData[] + status?: number + headers?: Record | Headers + /** + * @type {string} + * @example redirectTo: '/cart' + */ + redirectTo?: string +} export type APIHandlerContext< C extends CommerceAPI, @@ -16,14 +21,10 @@ export type APIHandlerContext< Data = any, Options extends {} = {} > = { - req: NextApiRequest - res: NextApiResponse> + req: NextRequest commerce: C config: C['provider']['config'] handlers: H - /** - * Custom configs that may be used by a particular handler - */ options: Options } @@ -35,7 +36,7 @@ export type APIHandler< Options extends {} = {} > = ( context: APIHandlerContext & { body: Body } -) => void | Promise +) => Promise> export type APIHandlers = { [k: string]: APIHandler @@ -46,4 +47,6 @@ export type APIEndpoint< H extends APIHandlers = {}, Data = any, Options extends {} = {} -> = (context: APIHandlerContext) => void | Promise +> = ( + context: APIHandlerContext +) => Promise> diff --git a/packages/commerce/src/api/utils/validate-handlers.ts b/packages/commerce/src/api/utils/validate-handlers.ts new file mode 100644 index 000000000..3e4ed2ab7 --- /dev/null +++ b/packages/commerce/src/api/utils/validate-handlers.ts @@ -0,0 +1,24 @@ +import type { NextRequest } from 'next/server' +import type { APIHandler } from './types' +import validateMethod, { HTTP_METHODS } from './validate-method' +/** + * Validates the request method and throws an error if it's not allowed, or if the handler is not implemented. + * and stops the execution of the handler. + * @param req The request object. + * @param allowedOperations An object containing the handlers for each method. + * @throws Error when the method is not allowed or the handler is not implemented. + */ +export default function validateHandlers( + req: NextRequest, + allowedOperations: { [k in HTTP_METHODS]?: APIHandler } +) { + const methods = Object.keys(allowedOperations) as HTTP_METHODS[] + const allowedMethods = methods.reduce((arr, method) => { + if (allowedOperations[method]) { + arr.push(method) + } + return arr + }, []) + + return validateMethod(req, allowedMethods) +} diff --git a/packages/commerce/src/api/utils/validate-method.ts b/packages/commerce/src/api/utils/validate-method.ts new file mode 100644 index 000000000..945427131 --- /dev/null +++ b/packages/commerce/src/api/utils/validate-method.ts @@ -0,0 +1,48 @@ +import type { NextRequest } from 'next/server' +import { CommerceAPIResponseError } from './errors' + +export type HTTP_METHODS = 'OPTIONS' | 'GET' | 'POST' | 'PUT' | 'DELETE' + +export default function validateMethod( + req: NextRequest, + allowedMethods: HTTP_METHODS[] +) { + const methods = allowedMethods.includes('OPTIONS') + ? allowedMethods + : [...allowedMethods, 'OPTIONS'] + + if (!req.method || !methods.includes(req.method)) { + throw new CommerceAPIResponseError( + `The HTTP ${req.method} method is not supported at this route.`, + new Response( + JSON.stringify({ + errors: [ + { + code: 'invalid_method', + message: `The HTTP ${req.method} method is not supported at this route.`, + }, + ], + }), + { + status: 405, + headers: { + Allow: methods.join(', '), + }, + } + ) + ) + } + + if (req.method === 'OPTIONS') { + throw new CommerceAPIResponseError( + 'This is a CORS preflight request.', + new Response(null, { + status: 204, + headers: { + Allow: methods.join(', '), + 'Content-Length': '0', + }, + }) + ) + } +} diff --git a/packages/commerce/src/schemas/auth.ts b/packages/commerce/src/schemas/auth.ts new file mode 100644 index 000000000..cab5cf897 --- /dev/null +++ b/packages/commerce/src/schemas/auth.ts @@ -0,0 +1,18 @@ +import { z } from 'zod' + +export const loginBodySchema = z.object({ + redirectTo: z.string().optional(), + email: z.string().email(), + password: z.string().min(7), +}) + +export const logoutBodySchema = z.object({ + redirectTo: z.string().optional(), +}) + +export const signupBodySchema = z.object({ + firstName: z.string(), + lastName: z.string(), + email: z.string().email(), + password: z.string().min(7), +}) diff --git a/packages/commerce/src/schemas/cart.ts b/packages/commerce/src/schemas/cart.ts new file mode 100644 index 000000000..69820c22a --- /dev/null +++ b/packages/commerce/src/schemas/cart.ts @@ -0,0 +1,102 @@ +import { z } from 'zod' + +export const getCartBodySchema = z.object({ + cartId: z.string().optional(), +}) + +export const cartItemBodySchema = z.object({ + variantId: z.string(), + productId: z.string().optional(), + quantity: z.number().min(1).optional(), +}) + +export const addItemBodySchema = z.object({ + cartId: z.string().optional(), + item: cartItemBodySchema, +}) + +export const updateItemBodySchema = z.object({ + cartId: z.string(), + itemId: z.string(), + item: cartItemBodySchema, +}) + +export const removeItemBodySchema = z.object({ + cartId: z.string(), + itemId: z.string(), +}) + +export const discountSchema = z.object({ + value: z.number(), +}) + +export const optionsSchema = z.object({ + id: z.string().optional(), + name: z.string(), + value: z.string(), +}) + +export const cartProductVariantSchema = z.object({ + id: z.string(), + sku: z.string().optional(), + name: z.string(), + price: z.number(), + listPrice: z.number(), + availableForSale: z.boolean().optional(), + requiresShipping: z.boolean().optional(), + image: z.object({ + url: z.string(), + altText: z.string().optional(), + }), + weight: z + .object({ + value: z.number(), + unit: z.string(), + }) + .optional(), + height: z + .object({ + value: z.number(), + unit: z.string(), + }) + .optional(), + width: z + .object({ + value: z.number(), + unit: z.string(), + }) + .optional(), + depth: z + .object({ + value: z.number(), + unit: z.string(), + }) + .optional(), +}) + +export const cartLineItemSchema = z.object({ + id: z.string(), + variantId: z.string(), + productId: z.string(), + name: z.string(), + quantity: z.number().min(1), + discounts: z.array(discountSchema).optional(), + path: z.string().startsWith('/').optional(), + variant: cartProductVariantSchema, + options: z.array(optionsSchema).optional(), +}) + +export const cartSchema = z.object({ + id: z.string(), + customerId: z.string().optional(), + url: z.string().optional(), + email: z.string().optional(), + createdAt: z.string(), + currency: z.object({ code: z.string() }), + taxesIncluded: z.boolean(), + lineItems: z.array(cartLineItemSchema), + lineItemsSubtotalPrice: z.number(), + subtotalPrice: z.number(), + totalPrice: z.number(), + discounts: z.array(discountSchema).optional(), +}) diff --git a/packages/commerce/src/schemas/checkout.ts b/packages/commerce/src/schemas/checkout.ts new file mode 100644 index 000000000..cb5f9b823 --- /dev/null +++ b/packages/commerce/src/schemas/checkout.ts @@ -0,0 +1,25 @@ +import { z } from 'zod' +import { cartLineItemSchema } from './cart' +import { addressFieldsSchema, cardFieldsSchema } from './customer' + +export const getCheckoutBodySchema = z.object({ + cartId: z.string().optional(), +}) + +export const submitCheckoutBodySchema = z.object({ + cartId: z.string(), + item: z.object({ + cartId: z.string().optional(), + card: cardFieldsSchema, + address: addressFieldsSchema, + }), +}) + +export const checkoutSchema = z.object({ + hasPayment: z.boolean(), + hasShipping: z.boolean(), + addressId: z.string(), + payments: z.array(cardFieldsSchema).optional(), + cardId: z.string().optional(), + lineItems: z.array(cartLineItemSchema).optional(), +}) diff --git a/packages/commerce/src/schemas/customer.ts b/packages/commerce/src/schemas/customer.ts new file mode 100644 index 000000000..a48ba06ab --- /dev/null +++ b/packages/commerce/src/schemas/customer.ts @@ -0,0 +1,84 @@ +import { z } from 'zod' + +export const getCustomerAddressBodySchema = z.object({ + cartId: z.string(), +}) + +export const customerSchema = z.object({ + id: z.string(), + firstName: z.string(), + lastName: z.string(), + email: z.string().optional(), + phone: z.string().optional(), + company: z.string().optional(), + notes: z.string().optional(), + acceptsMarketing: z.boolean().optional(), +}) + +export const addressSchema = z.object({ + id: z.string(), + mask: z.string(), +}) + +export const addressFieldsSchema = z.object({ + type: z.string(), + firstName: z.string(), + lastName: z.string(), + company: z.string(), + streetNumber: z.string(), + apartments: z.string(), + zipCode: z.string(), + city: z.string(), + country: z.string(), +}) + +export const addAddressBodySchema = z.object({ + cartId: z.string(), + item: addressFieldsSchema, +}) + +export const updateAddressBodySchema = z.object({ + cartId: z.string(), + itemId: z.string(), + item: addressFieldsSchema, +}) + +export const deleteAddressBodySchema = z.object({ + cartId: z.string(), + itemId: z.string(), +}) + +export const cardFieldsSchema = z.object({ + cardHolder: z.string(), + cardNumber: z.string(), + cardExpireDate: z.string(), + cardCvc: z.string(), + firstName: z.string(), + lastName: z.string(), + company: z.string(), + streetNumber: z.string(), + zipCode: z.string(), + city: z.string(), + country: z.string(), +}) + +export const cardSchema = z.object({ + id: z.string(), + mask: z.string(), +}) + +export const addCardBodySchema = z.object({ + cartId: z.string(), + item: cardFieldsSchema, +}) + +export const updateCardBodySchema = z.object({ + cartId: z.string(), + itemId: z.string(), + item: cardFieldsSchema, +}) + +export const deleteCardBodySchema = z.object({ + cartId: z.string(), + itemId: z.string(), +}) diff --git a/packages/commerce/src/schemas/product.ts b/packages/commerce/src/schemas/product.ts index ca02158e9..ff10d9b46 100644 --- a/packages/commerce/src/schemas/product.ts +++ b/packages/commerce/src/schemas/product.ts @@ -1,4 +1,4 @@ -import { z } from 'zod' +import { boolean, z } from 'zod' export const productPriceSchema = z.object({ value: z.number(), @@ -52,9 +52,14 @@ export const productsPathsSchema = z.array( ) export const searchProductBodySchema = z.object({ - search: z.string(), - categoryId: z.string(), + search: z.string().optional(), + categoryId: z.string().optional(), brandId: z.string().optional(), sort: z.string().optional(), locale: z.string().optional(), }) + +export const searchProductsSchema = z.object({ + products: z.array(productSchema), + found: z.boolean(), +}) diff --git a/packages/commerce/src/schemas/whishlist.ts b/packages/commerce/src/schemas/whishlist.ts new file mode 100644 index 000000000..f1b406235 --- /dev/null +++ b/packages/commerce/src/schemas/whishlist.ts @@ -0,0 +1,41 @@ +import { z } from 'zod' +import { productSchema } from './product' + +export const wishlistSchemaItem = z.object({ + id: z.string(), + productId: z.string(), + variantId: z.string(), + product: productSchema, +}) + +export const wishlistSchema = z.object({ + id: z.string(), + items: z.array(wishlistSchemaItem), + token: z.string().optional(), +}) + +export const getWishlistBodySchema = z.object({ + customerAccessToken: z.string(), + includeProducts: z.boolean(), +}) + +export const wishlistItemBodySchema = z.object({ + productId: z.string(), + variantId: z.string(), +}) + +export const addItemBodySchema = z.object({ + cartId: z.string().optional(), + item: wishlistItemBodySchema, +}) + +export const updateItemBodySchema = z.object({ + cartId: z.string(), + itemId: z.string(), + item: wishlistItemBodySchema, +}) + +export const removeItemBodySchema = z.object({ + cartId: z.string(), + itemId: z.string(), +}) diff --git a/packages/commerce/src/types/cart.ts b/packages/commerce/src/types/cart.ts index 487d74679..014fed873 100644 --- a/packages/commerce/src/types/cart.ts +++ b/packages/commerce/src/types/cart.ts @@ -199,14 +199,14 @@ export type CartHooks = { } export type GetCartHook = { - data: Cart | null + data: Cart | null | undefined input: {} fetcherInput: { cartId?: string } swrState: { isEmpty: boolean } } export type AddItemHook = { - data: Cart + data: Cart | null | undefined input?: CartItemBody fetcherInput: CartItemBody body: { item: CartItemBody } @@ -251,7 +251,8 @@ export type GetCartHandler = GetCartHook & { } export type AddItemHandler = AddItemHook & { - body: { cartId: string } + data: Cart | null | undefined + body: { cartId?: string } } export type UpdateItemHandler = UpdateItemHook & { diff --git a/packages/commerce/src/types/checkout.ts b/packages/commerce/src/types/checkout.ts index 8b5d428fc..326af252e 100644 --- a/packages/commerce/src/types/checkout.ts +++ b/packages/commerce/src/types/checkout.ts @@ -48,7 +48,7 @@ export interface CheckoutBody { } export type SubmitCheckoutHook = { - data: Checkout + data: Checkout | null input?: CheckoutBody fetcherInput: CheckoutBody body: { item: CheckoutBody } @@ -69,7 +69,7 @@ export type CheckoutHooks = { } export type GetCheckoutHandler = GetCheckoutHook & { - body: { cartId: string } + body: { cartId?: string } } export type SubmitCheckoutHandler = SubmitCheckoutHook & { diff --git a/packages/commerce/src/types/customer/address.ts b/packages/commerce/src/types/customer/address.ts index ec4aace50..e6b71ff52 100644 --- a/packages/commerce/src/types/customer/address.ts +++ b/packages/commerce/src/types/customer/address.ts @@ -61,7 +61,7 @@ export type GetAddressesHook = { } export type AddItemHook = { - data: Address + data: Address | null input?: AddressFields fetcherInput: AddressFields body: { item: AddressFields } @@ -77,7 +77,7 @@ export type UpdateItemHook = { } export type RemoveItemHook = { - data: Address | null | undefined + data: Address | null input: { item?: Address } fetcherInput: { itemId: string } body: { itemId: string } @@ -100,7 +100,6 @@ export type AddItemHandler = AddItemHook & { } export type UpdateItemHandler = UpdateItemHook & { - data: Address body: { cartId: string } } diff --git a/packages/commerce/src/types/customer/card.ts b/packages/commerce/src/types/customer/card.ts index 11956ffcc..37a134de8 100644 --- a/packages/commerce/src/types/customer/card.ts +++ b/packages/commerce/src/types/customer/card.ts @@ -78,7 +78,7 @@ export type GetCardsHook = { } export type AddItemHook = { - data: Card + data: Card | null input?: CardFields fetcherInput: CardFields body: { item: CardFields } @@ -86,7 +86,7 @@ export type AddItemHook = { } export type UpdateItemHook = { - data: Card | null | undefined + data: Card | null input: { item?: CardFields; wait?: number } fetcherInput: { itemId: string; item: CardFields } body: { itemId: string; item: CardFields } @@ -94,7 +94,7 @@ export type UpdateItemHook = { } export type RemoveItemHook = { - data: Card | null | undefined + data: Card | null input: { item?: Card } fetcherInput: { itemId: string } body: { itemId: string } @@ -116,7 +116,6 @@ export type AddItemHandler = AddItemHook & { } export type UpdateItemHandler = UpdateItemHook & { - data: Card body: { cartId: string } } diff --git a/packages/commerce/src/utils/types.ts b/packages/commerce/src/utils/types.ts index 317fea165..578e04094 100644 --- a/packages/commerce/src/utils/types.ts +++ b/packages/commerce/src/utils/types.ts @@ -55,7 +55,7 @@ export type HookFetcherOptions = { method?: string } & ( | { query?: string; url: string } ) -export type HookInputValue = string | number | boolean | undefined +export type HookInputValue = string | number | boolean | null | undefined export type HookSWRInput = [string, HookInputValue][] diff --git a/packages/commercejs/src/api/endpoints/cart/index.ts b/packages/commercejs/src/api/endpoints/cart/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/commercejs/src/api/endpoints/cart/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/commercejs/src/api/endpoints/catalog/index.ts b/packages/commercejs/src/api/endpoints/catalog/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/commercejs/src/api/endpoints/catalog/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/commercejs/src/api/endpoints/catalog/products/index.ts b/packages/commercejs/src/api/endpoints/catalog/products/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/commercejs/src/api/endpoints/catalog/products/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/commercejs/src/api/endpoints/checkout/get-checkout.ts b/packages/commercejs/src/api/endpoints/checkout/get-checkout.ts index 491bf0ac9..f6923f0e3 100644 --- a/packages/commercejs/src/api/endpoints/checkout/get-checkout.ts +++ b/packages/commercejs/src/api/endpoints/checkout/get-checkout.ts @@ -1 +1,3 @@ -export default function noopApi(...args: any[]): void {} +export default function getCheckout(..._args: any[]) { + return Promise.resolve({ data: null }) +} diff --git a/packages/commercejs/src/api/endpoints/checkout/index.ts b/packages/commercejs/src/api/endpoints/checkout/index.ts index 6a4eb4864..1536c130a 100644 --- a/packages/commercejs/src/api/endpoints/checkout/index.ts +++ b/packages/commercejs/src/api/endpoints/checkout/index.ts @@ -3,16 +3,15 @@ import checkoutEndpoint from '@vercel/commerce/api/endpoints/checkout' import type { CheckoutSchema } from '@vercel/commerce/types/checkout' import type { CommercejsAPI } from '../..' -import submitCheckout from './submit-checkout' import getCheckout from './get-checkout' +import submitCheckout from './submit-checkout' export type CheckoutAPI = GetAPISchema - export type CheckoutEndpoint = CheckoutAPI['endpoint'] export const handlers: CheckoutEndpoint['handlers'] = { - submitCheckout, getCheckout, + submitCheckout, } const checkoutApi = createEndpoint({ diff --git a/packages/commercejs/src/api/endpoints/checkout/submit-checkout.ts b/packages/commercejs/src/api/endpoints/checkout/submit-checkout.ts index e0f3012cc..1af220cd0 100644 --- a/packages/commercejs/src/api/endpoints/checkout/submit-checkout.ts +++ b/packages/commercejs/src/api/endpoints/checkout/submit-checkout.ts @@ -5,7 +5,6 @@ import sdkFetcherFunction from '../../utils/sdk-fetch' import { normalizeTestCheckout } from '../../../utils/normalize-checkout' const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({ - res, body: { item, cartId }, config: { sdkFetch }, }) => { @@ -38,7 +37,7 @@ const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({ // Capture the order await sdkFetcher('checkout', 'capture', checkoutToken, checkoutData) - res.status(200).json({ data: null, errors: [] }) + return { data: null } } export default submitCheckout diff --git a/packages/commercejs/src/api/endpoints/customer/address/index.ts b/packages/commercejs/src/api/endpoints/customer/address/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/commercejs/src/api/endpoints/customer/address/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/commercejs/src/api/endpoints/customer/card/index.ts b/packages/commercejs/src/api/endpoints/customer/card/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/commercejs/src/api/endpoints/customer/card/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/commercejs/src/api/endpoints/customer/index.ts b/packages/commercejs/src/api/endpoints/customer/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/commercejs/src/api/endpoints/customer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/commercejs/src/api/endpoints/index.ts b/packages/commercejs/src/api/endpoints/index.ts new file mode 100644 index 000000000..2e754505e --- /dev/null +++ b/packages/commercejs/src/api/endpoints/index.ts @@ -0,0 +1,15 @@ +import type { CommercejsAPI } from '..' + +import createEndpoints from '@vercel/commerce/api/endpoints' + +import login from './login' +import checkout from './checkout' + +const endpoints = { + login, + checkout, +} + +export default function commercejsAPI(commerce: CommercejsAPI) { + return createEndpoints(commerce, endpoints) +} diff --git a/packages/commercejs/src/api/endpoints/login/login.ts b/packages/commercejs/src/api/endpoints/login/login.ts index b9088ad22..c2abbc5cb 100644 --- a/packages/commercejs/src/api/endpoints/login/login.ts +++ b/packages/commercejs/src/api/endpoints/login/login.ts @@ -1,32 +1,33 @@ +import type { LoginEndpoint } from '.' + import { serialize } from 'cookie' + import sdkFetcherFunction from '../../utils/sdk-fetch' import { getDeploymentUrl } from '../../../utils/get-deployment-url' -import type { LoginEndpoint } from '.' const login: LoginEndpoint['handlers']['login'] = async ({ req, - res, config: { sdkFetch, customerCookie }, }) => { const sdkFetcher: typeof sdkFetcherFunction = sdkFetch const redirectUrl = getDeploymentUrl() - try { - const loginToken = req.query?.token as string - if (!loginToken) { - res.redirect(redirectUrl) - } - const { jwt } = await sdkFetcher('customer', 'getToken', loginToken, false) - res.setHeader( - 'Set-Cookie', - serialize(customerCookie, jwt, { + const { searchParams } = new URL(req.url) + const loginToken = searchParams.get('token') + + if (!loginToken) { + return { redirectTo: redirectUrl } + } + + const { jwt } = await sdkFetcher('customer', 'getToken', loginToken, false) + + return { + headers: { + 'Set-Cookie': serialize(customerCookie, jwt, { secure: process.env.NODE_ENV === 'production', maxAge: 60 * 60 * 24, path: '/', - }) - ) - res.redirect(redirectUrl) - } catch { - res.redirect(redirectUrl) + }), + }, } } diff --git a/packages/commercejs/src/api/endpoints/logout/index.ts b/packages/commercejs/src/api/endpoints/logout/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/commercejs/src/api/endpoints/logout/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/commercejs/src/api/endpoints/signup/index.ts b/packages/commercejs/src/api/endpoints/signup/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/commercejs/src/api/endpoints/signup/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/commercejs/src/api/endpoints/wishlist/index.tsx b/packages/commercejs/src/api/endpoints/wishlist/index.tsx deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/commercejs/src/api/endpoints/wishlist/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/commercejs/src/checkout/use-submit-checkout.tsx b/packages/commercejs/src/checkout/use-submit-checkout.tsx index 2cb723281..3a2b2c6fe 100644 --- a/packages/commercejs/src/checkout/use-submit-checkout.tsx +++ b/packages/commercejs/src/checkout/use-submit-checkout.tsx @@ -11,7 +11,7 @@ export default useSubmitCheckout as UseSubmitCheckout export const handler: MutationHook = { fetchOptions: { - url: '/api/checkout', + url: '/api/commerce/checkout', method: 'POST', }, async fetcher({ input: item, options, fetch }) { diff --git a/packages/commercejs/src/customer/use-customer.tsx b/packages/commercejs/src/customer/use-customer.tsx index 57d8f702b..bfcf79193 100644 --- a/packages/commercejs/src/customer/use-customer.tsx +++ b/packages/commercejs/src/customer/use-customer.tsx @@ -1,11 +1,16 @@ +import type { SWRHook } from '@vercel/commerce/utils/types' +import type { CustomerHook } from '@vercel/commerce/types/customer' + import Cookies from 'js-cookie' -import { decode } from 'jsonwebtoken' -import { SWRHook } from '@vercel/commerce/utils/types' +import { decode, type JwtPayload } from 'jsonwebtoken' import useCustomer, { - UseCustomer, + type UseCustomer, } from '@vercel/commerce/customer/use-customer' import { CUSTOMER_COOKIE, API_URL } from '../constants' -import type { CustomerHook } from '@vercel/commerce/types/customer' + +type JwtData = JwtPayload & { + cid: string +} export default useCustomer as UseCustomer export const handler: SWRHook = { @@ -20,7 +25,8 @@ export const handler: SWRHook = { return null } - const decodedToken = decode(token) as { cid: string } + const decodedToken = decode(token) as JwtData + const customer = await fetch({ query: options.query, method: options.method, diff --git a/packages/commercejs/src/utils/product-search.ts b/packages/commercejs/src/utils/product-search.ts index 9a4d42982..ebef3f7ed 100644 --- a/packages/commercejs/src/utils/product-search.ts +++ b/packages/commercejs/src/utils/product-search.ts @@ -4,8 +4,8 @@ const getFilterVariables = ({ search, categoryId, }: { - search?: string - categoryId?: string | number + search?: string | null + categoryId?: string | number | null }) => { let filterVariables: { [key: string]: any } = {} if (search) { @@ -17,7 +17,7 @@ const getFilterVariables = ({ return filterVariables } -const getSortVariables = ({ sort }: { sort?: string }) => { +const getSortVariables = ({ sort }: { sort?: string | null }) => { let sortVariables: { [key: string]: any } = {} switch (sort) { case 'trending-desc': diff --git a/packages/kibocommerce/package.json b/packages/kibocommerce/package.json index 7cae01524..de4217ec1 100644 --- a/packages/kibocommerce/package.json +++ b/packages/kibocommerce/package.json @@ -50,9 +50,7 @@ }, "dependencies": { "@vercel/commerce": "workspace:*", - "@vercel/fetch": "^6.2.0", - "lodash.debounce": "^4.0.8", - "node-fetch": "^2.6.7" + "lodash.debounce": "^4.0.8" }, "peerDependencies": { "next": "^12", diff --git a/packages/kibocommerce/src/api/endpoints/cart/add-item.ts b/packages/kibocommerce/src/api/endpoints/cart/add-item.ts index 6dfea1e0c..d1609179d 100644 --- a/packages/kibocommerce/src/api/endpoints/cart/add-item.ts +++ b/packages/kibocommerce/src/api/endpoints/cart/add-item.ts @@ -53,28 +53,19 @@ const buildAddToCartVariables = ({ const addItem: CartEndpoint['handlers']['addItem'] = async ({ req, - res, - body: { cartId, item }, + body: { item }, config, }) => { - if (!item) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Missing item' }], - }) - } - if (!item.quantity) item.quantity = 1 - const productResponse = await config.fetch(getProductQuery, { variables: { productCode: item?.productId }, }) - const cookieHandler = new CookieHandler(config, req, res) + const cookieHandler = new CookieHandler(config, req) let accessToken = null if (!cookieHandler.getAccessToken()) { let anonymousShopperTokenResponse = await cookieHandler.getAnonymousToken() - accessToken = anonymousShopperTokenResponse.accessToken; + accessToken = anonymousShopperTokenResponse.accessToken } else { accessToken = cookieHandler.getAccessToken() } @@ -95,7 +86,8 @@ const addItem: CartEndpoint['handlers']['addItem'] = async ({ ) currentCart = result?.data?.currentCart } - res.status(200).json({ data: normalizeCart(currentCart) }) + + return { data: normalizeCart(currentCart) } } export default addItem diff --git a/packages/kibocommerce/src/api/endpoints/cart/get-cart.ts b/packages/kibocommerce/src/api/endpoints/cart/get-cart.ts index 6a6ec3ab4..f121f931b 100644 --- a/packages/kibocommerce/src/api/endpoints/cart/get-cart.ts +++ b/packages/kibocommerce/src/api/endpoints/cart/get-cart.ts @@ -6,17 +6,17 @@ import { getCartQuery } from '../../queries/get-cart-query' const getCart: CartEndpoint['handlers']['getCart'] = async ({ req, - res, - body: { cartId }, config, }) => { let currentCart: Cart = {} + let headers try { - const cookieHandler = new CookieHandler(config, req, res) + const cookieHandler = new CookieHandler(config, req) let accessToken = null if (!cookieHandler.getAccessToken()) { - let anonymousShopperTokenResponse = await cookieHandler.getAnonymousToken() + let anonymousShopperTokenResponse = + await cookieHandler.getAnonymousToken() const response = anonymousShopperTokenResponse.response accessToken = anonymousShopperTokenResponse.accessToken cookieHandler.setAnonymousShopperCookie(response) @@ -30,12 +30,14 @@ const getCart: CartEndpoint['handlers']['getCart'] = async ({ { headers: { 'x-vol-user-claims': accessToken } } ) currentCart = result?.data?.currentCart + headers = cookieHandler.headers } catch (error) { throw error } - res.status(200).json({ + + return { data: currentCart ? normalizeCart(currentCart) : null, - }) + } } export default getCart diff --git a/packages/kibocommerce/src/api/endpoints/cart/index.ts b/packages/kibocommerce/src/api/endpoints/cart/index.ts index d4e8118ac..191ce2130 100644 --- a/packages/kibocommerce/src/api/endpoints/cart/index.ts +++ b/packages/kibocommerce/src/api/endpoints/cart/index.ts @@ -1,8 +1,8 @@ import { GetAPISchema, createEndpoint } from '@vercel/commerce/api' import cartEndpoint from '@vercel/commerce/api/endpoints/cart' import type { KiboCommerceAPI } from '../..' -import getCart from './get-cart'; -import addItem from './add-item'; +import getCart from './get-cart' +import addItem from './add-item' import updateItem from './update-item' import removeItem from './remove-item' diff --git a/packages/kibocommerce/src/api/endpoints/cart/remove-item.ts b/packages/kibocommerce/src/api/endpoints/cart/remove-item.ts index 62f6afdc6..089a0a00e 100644 --- a/packages/kibocommerce/src/api/endpoints/cart/remove-item.ts +++ b/packages/kibocommerce/src/api/endpoints/cart/remove-item.ts @@ -5,17 +5,10 @@ import { getCartQuery } from '../../../api/queries/get-cart-query' const removeItem: CartEndpoint['handlers']['removeItem'] = async ({ req, - res, - body: { cartId, itemId }, + body: { itemId }, config, }) => { - if (!itemId) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) - } - const encodedToken = req.cookies[config.customerCookie] + const encodedToken = req.cookies.get(config.customerCookie) const token = encodedToken ? Buffer.from(encodedToken, 'base64').toString('ascii') : null @@ -39,7 +32,10 @@ const removeItem: CartEndpoint['handlers']['removeItem'] = async ({ ) currentCart = result?.data?.currentCart } - res.status(200).json({ data: normalizeCart(currentCart) }) + + return { + data: normalizeCart(currentCart), + } } export default removeItem diff --git a/packages/kibocommerce/src/api/endpoints/cart/update-item.ts b/packages/kibocommerce/src/api/endpoints/cart/update-item.ts index b42ff3430..a665f4992 100644 --- a/packages/kibocommerce/src/api/endpoints/cart/update-item.ts +++ b/packages/kibocommerce/src/api/endpoints/cart/update-item.ts @@ -5,17 +5,10 @@ import updateCartItemQuantityMutation from '../../../api/mutations/updateCartIte const updateItem: CartEndpoint['handlers']['updateItem'] = async ({ req, - res, - body: { cartId, itemId, item }, + body: { itemId, item }, config, }) => { - if (!itemId || !item) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) - } - const encodedToken = req.cookies[config.customerCookie] + const encodedToken = req.cookies.get(config.cartCookie) const token = encodedToken ? Buffer.from(encodedToken, 'base64').toString('ascii') : null @@ -39,7 +32,8 @@ const updateItem: CartEndpoint['handlers']['updateItem'] = async ({ ) currentCart = result?.data?.currentCart } - res.status(200).json({ data: normalizeCart(currentCart) }) + + return { data: normalizeCart(currentCart) } } export default updateItem diff --git a/packages/kibocommerce/src/api/endpoints/catalog/products/products.ts b/packages/kibocommerce/src/api/endpoints/catalog/products/products.ts index 189922d53..8009f82f1 100644 --- a/packages/kibocommerce/src/api/endpoints/catalog/products/products.ts +++ b/packages/kibocommerce/src/api/endpoints/catalog/products/products.ts @@ -2,16 +2,15 @@ import { Product } from '@vercel/commerce/types/product' import { ProductsEndpoint } from '.' import productSearchQuery from '../../../queries/product-search-query' import { buildProductSearchVars } from '../../../../lib/product-search-vars' -import {normalizeProduct} from '../../../../lib/normalize' +import { normalizeProduct } from '../../../../lib/normalize' const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({ - res, body: { search, categoryId, brandId, sort }, config, }) => { - const pageSize = 100; - const filters = {}; - const startIndex = 0; + const pageSize = 100 + const filters = {} + const startIndex = 0 const variables = buildProductSearchVars({ categoryCode: categoryId, pageSize, @@ -20,12 +19,14 @@ const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({ filters, startIndex, }) - const {data} = await config.fetch(productSearchQuery, { variables }); - const found = data?.products?.items?.length > 0 ? true : false; - let productsResponse= data?.products?.items.map((item: any) =>normalizeProduct(item,config)); - const products: Product[] = found ? productsResponse : []; + const { data } = await config.fetch(productSearchQuery, { variables }) + const found = data?.products?.items?.length > 0 ? true : false + let productsResponse = data?.products?.items.map((item: any) => + normalizeProduct(item, config) + ) + const products: Product[] = found ? productsResponse : [] - res.status(200).json({ data: { products, found } }); + return { data: { products, found } } } export default getProducts diff --git a/packages/kibocommerce/src/api/endpoints/checkout/index.ts b/packages/kibocommerce/src/api/endpoints/checkout/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/kibocommerce/src/api/endpoints/checkout/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/kibocommerce/src/api/endpoints/customer/address.ts b/packages/kibocommerce/src/api/endpoints/customer/address.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/kibocommerce/src/api/endpoints/customer/address.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/kibocommerce/src/api/endpoints/customer/card.ts b/packages/kibocommerce/src/api/endpoints/customer/card.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/kibocommerce/src/api/endpoints/customer/card.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/kibocommerce/src/api/endpoints/customer/customer.ts b/packages/kibocommerce/src/api/endpoints/customer/customer.ts index c2e6db707..f8a8106cc 100644 --- a/packages/kibocommerce/src/api/endpoints/customer/customer.ts +++ b/packages/kibocommerce/src/api/endpoints/customer/customer.ts @@ -2,35 +2,32 @@ import CookieHandler from '../../../api/utils/cookie-handler' import type { CustomerEndpoint } from '.' import { getCustomerAccountQuery } from '../../queries/get-customer-account-query' import { normalizeCustomer } from '../../../lib/normalize' +import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' -const getLoggedInCustomer: CustomerEndpoint['handlers']['getLoggedInCustomer'] = async ({ - req, - res, - config, -}) => { - const cookieHandler = new CookieHandler(config, req, res) - let accessToken = cookieHandler.getAccessToken(); +const getLoggedInCustomer: CustomerEndpoint['handlers']['getLoggedInCustomer'] = + async ({ req, config }) => { + const cookieHandler = new CookieHandler(config, req) + let accessToken = cookieHandler.getAccessToken() - if (!cookieHandler.isShopperCookieAnonymous()) { - const { data } = await config.fetch(getCustomerAccountQuery, undefined, { - headers: { - 'x-vol-user-claims': accessToken, - }, - }) - - const customer = normalizeCustomer(data?.customerAccount) - - if (!customer.id) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Customer not found', code: 'not_found' }], + if (!cookieHandler.isShopperCookieAnonymous()) { + const { data } = await config.fetch(getCustomerAccountQuery, undefined, { + headers: { + 'x-vol-user-claims': accessToken, + }, }) + + const customer = normalizeCustomer(data?.customerAccount) + + if (!customer.id) { + throw new CommerceAPIError('Customer not found', { + status: 404, + }) + } + + return { data: { customer } } } - return res.status(200).json({ data: { customer } }) + return { data: null } } - res.status(200).json({ data: null }) -} - export default getLoggedInCustomer diff --git a/packages/kibocommerce/src/api/endpoints/index.ts b/packages/kibocommerce/src/api/endpoints/index.ts new file mode 100644 index 000000000..3160f3da6 --- /dev/null +++ b/packages/kibocommerce/src/api/endpoints/index.ts @@ -0,0 +1,25 @@ +import type { KiboCommerceAPI } from '..' + +import createEndpoints from '@vercel/commerce/api/endpoints' + +import cart from './cart' +import login from './login' +import logout from './logout' +import signup from './signup' +import customer from './customer' +import wishlist from './wishlist' +import products from './catalog/products' + +const endpoints = { + cart, + login, + logout, + signup, + wishlist, + customer, + 'catalog/products': products, +} + +export default function kiboCommerceAPI(commerce: KiboCommerceAPI) { + return createEndpoints(commerce, endpoints) +} diff --git a/packages/kibocommerce/src/api/endpoints/login/login.ts b/packages/kibocommerce/src/api/endpoints/login/login.ts index 7a08069c6..f88f8d2a2 100644 --- a/packages/kibocommerce/src/api/endpoints/login/login.ts +++ b/packages/kibocommerce/src/api/endpoints/login/login.ts @@ -1,66 +1,53 @@ -import { FetcherError } from '@vercel/commerce/utils/errors' import type { LoginEndpoint } from '.' + +import { FetcherError } from '@vercel/commerce/utils/errors' +import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' + import { loginMutation } from '../../mutations/login-mutation' -import { prepareSetCookie } from '../../../lib/prepare-set-cookie'; -import { setCookies } from '../../../lib/set-cookie' +import { prepareSetCookie } from '../../../lib/prepare-set-cookie' import { getCookieExpirationDate } from '../../../lib/get-cookie-expiration-date' const invalidCredentials = /invalid credentials/i const login: LoginEndpoint['handlers']['login'] = async ({ - req, - res, body: { email, password }, config, - commerce, }) => { - - if (!(email && password)) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) - } - - let response; + let response try { - - const variables = { loginInput : { username: email, password }}; - response = await config.fetch(loginMutation, { variables }) - const { account: token } = response.data; + const variables = { loginInput: { username: email, password } } + response = await config.fetch(loginMutation, { variables }) + const { account: token } = response.data // Set Cookie - const cookieExpirationDate = getCookieExpirationDate(config.customerCookieMaxAgeInDays) + const cookieExpirationDate = getCookieExpirationDate( + config.customerCookieMaxAgeInDays + ) const authCookie = prepareSetCookie( config.customerCookie, JSON.stringify(token), - token.accessTokenExpiration ? { expires: cookieExpirationDate }: {}, + token.accessTokenExpiration ? { expires: cookieExpirationDate } : {} ) - setCookies(res, [authCookie]) + return { data: null, headers: { 'Set-Cookie': authCookie } } } catch (error) { // Check if the email and password didn't match an existing account if ( error instanceof FetcherError && invalidCredentials.test(error.message) ) { - return res.status(401).json({ - data: null, - errors: [ - { - message: - 'Cannot find an account that matches the provided credentials', - code: 'invalid_credentials', - }, - ], - }) + throw new CommerceAPIError( + 'Cannot find an account that matches the provided credentials', + { + status: 401, + code: 'invalid_credentials', + } + ) + } else { + throw error } - - throw error } - - res.status(200).json({ data: response }) } export default login diff --git a/packages/kibocommerce/src/api/endpoints/logout/logout.ts b/packages/kibocommerce/src/api/endpoints/logout/logout.ts index 1b0835e39..27b56a2c1 100644 --- a/packages/kibocommerce/src/api/endpoints/logout/logout.ts +++ b/packages/kibocommerce/src/api/endpoints/logout/logout.ts @@ -1,22 +1,22 @@ import type { LogoutEndpoint } from '.' -import {prepareSetCookie} from '../../../lib/prepare-set-cookie'; -import {setCookies} from '../../../lib/set-cookie' +import { prepareSetCookie } from '../../../lib/prepare-set-cookie' const logout: LogoutEndpoint['handlers']['logout'] = async ({ - res, body: { redirectTo }, config, }) => { // Remove the cookie - const authCookie = prepareSetCookie(config.customerCookie,'',{ maxAge: -1, path: '/' }) - setCookies(res, [authCookie]) + const authCookie = prepareSetCookie(config.customerCookie, '', { + maxAge: -1, + path: '/', + }) + + const headers = { + 'Set-Cookie': authCookie, + } // Only allow redirects to a relative URL - if (redirectTo?.startsWith('/')) { - res.redirect(redirectTo) - } else { - res.status(200).json({ data: null }) - } + return redirectTo?.startsWith('/') ? { redirectTo, headers } : { headers } } export default logout diff --git a/packages/kibocommerce/src/api/endpoints/signup/signup.ts b/packages/kibocommerce/src/api/endpoints/signup/signup.ts index b3d988475..62d46838f 100644 --- a/packages/kibocommerce/src/api/endpoints/signup/signup.ts +++ b/packages/kibocommerce/src/api/endpoints/signup/signup.ts @@ -1,91 +1,89 @@ -import { FetcherError } from '@vercel/commerce/utils/errors' import type { SignupEndpoint } from '.' -import { registerUserMutation, registerUserLoginMutation } from '../../mutations/signup-mutation' -import { prepareSetCookie } from '../../../lib/prepare-set-cookie'; -import { setCookies } from '../../../lib/set-cookie' + +import { FetcherError } from '@vercel/commerce/utils/errors' +import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' + +import { + registerUserMutation, + registerUserLoginMutation, +} from '../../mutations/signup-mutation' +import { prepareSetCookie } from '../../../lib/prepare-set-cookie' import { getCookieExpirationDate } from '../../../lib/get-cookie-expiration-date' const invalidCredentials = /invalid credentials/i const signup: SignupEndpoint['handlers']['signup'] = async ({ - req, - res, body: { email, password, firstName, lastName }, config, - commerce, }) => { - - if (!(email && password)) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) - } - - let response; + let response try { - // Register user const registerUserVariables = { customerAccountInput: { - emailAddress: email, - firstName: firstName, - lastName: lastName, - acceptsMarketing: true, - id: 0 - } + emailAddress: email, + firstName: firstName, + lastName: lastName, + acceptsMarketing: true, + id: 0, + }, } - const registerUserResponse = await config.fetch(registerUserMutation, { variables: registerUserVariables}) - const accountId = registerUserResponse.data?.account?.id; + const registerUserResponse = await config.fetch(registerUserMutation, { + variables: registerUserVariables, + }) + const accountId = registerUserResponse.data?.account?.id // Login user const registerUserLoginVairables = { accountId: accountId, customerLoginInfoInput: { - emailAddress: email, - username: email, - password: password, - isImport: false - } + emailAddress: email, + username: email, + password: password, + isImport: false, + }, } - response = await config.fetch(registerUserLoginMutation, { variables: registerUserLoginVairables}) - const { account: token } = response.data; + response = await config.fetch(registerUserLoginMutation, { + variables: registerUserLoginVairables, + }) + const { account: token } = response.data // Set Cookie - const cookieExpirationDate = getCookieExpirationDate(config.customerCookieMaxAgeInDays) + const cookieExpirationDate = getCookieExpirationDate( + config.customerCookieMaxAgeInDays + ) const authCookie = prepareSetCookie( config.customerCookie, JSON.stringify(token), - token.accessTokenExpiration ? { expires: cookieExpirationDate }: {}, + token.accessTokenExpiration ? { expires: cookieExpirationDate } : {} ) - setCookies(res, [authCookie]) - + return { + data: response, + headers: { + 'Set-Cookie': authCookie, + }, + } } catch (error) { // Check if the email and password didn't match an existing account if ( error instanceof FetcherError && invalidCredentials.test(error.message) ) { - return res.status(401).json({ - data: null, - errors: [ - { - message: - 'Cannot find an account that matches the provided credentials', - code: 'invalid_credentials', - }, - ], - }) + throw new CommerceAPIError( + 'Cannot find an account that matches the provided credentials', + { + status: 401, + code: 'invalid_credentials', + } + ) + } else { + throw error } - - throw error } - - res.status(200).json({ data: response }) } export default signup diff --git a/packages/kibocommerce/src/api/endpoints/wishlist/add-item.ts b/packages/kibocommerce/src/api/endpoints/wishlist/add-item.ts index 49cfc37d5..f761c519c 100644 --- a/packages/kibocommerce/src/api/endpoints/wishlist/add-item.ts +++ b/packages/kibocommerce/src/api/endpoints/wishlist/add-item.ts @@ -1,17 +1,20 @@ -import getCustomerWishlist from '../../operations/get-customer-wishlist' -import getCustomerId from '../../utils/get-customer-id' import type { WishlistEndpoint } from '.' + +import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' + import { normalizeWishlistItem } from '../../../lib/normalize' import { getProductQuery } from '../../../api/queries/get-product-query' -import addItemToWishlistMutation from '../../mutations/addItemToWishlist-mutation' + +import getCustomerId from '../../utils/get-customer-id' import createWishlist from '../../mutations/create-wishlist-mutation' +import addItemToWishlistMutation from '../../mutations/addItemToWishlist-mutation' // Return wishlist info const buildAddToWishlistVariables = ({ productId, variantId, productResponse, - wishlist + wishlist, }: { productId: string variantId: string @@ -23,7 +26,7 @@ const buildAddToWishlistVariables = ({ const selectedOptions = product.variations?.find( (v: any) => v.productCode === variantId ).options - const quantity=1 + const quantity = 1 let options: any[] = [] selectedOptions?.forEach((each: any) => { product?.options @@ -47,53 +50,50 @@ const buildAddToWishlistVariables = ({ productCode: productId, variationProductCode: variantId ? variantId : null, options, - } }, + }, } } const addItem: WishlistEndpoint['handlers']['addItem'] = async ({ - res, body: { customerToken, item }, config, commerce, }) => { - const token = customerToken ? Buffer.from(customerToken, 'base64').toString('ascii'): null; - const accessToken = token ? JSON.parse(token).accessToken : null; + const token = customerToken + ? Buffer.from(customerToken, 'base64').toString('ascii') + : null + const accessToken = token ? JSON.parse(token).accessToken : null let result: { data?: any } = {} let wishlist: any - if (!item) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Missing item' }], - }) - } - - const customerId = customerToken && (await getCustomerId({ customerToken, config })) - const wishlistName= config.defaultWishlistName + const customerId = + customerToken && (await getCustomerId({ customerToken, config })) + const wishlistName = config.defaultWishlistName if (!customerId) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) + throw new CommerceAPIError('Customer not found', { status: 404 }) } const wishlistResponse = await commerce.getCustomerWishlist({ variables: { customerId, wishlistName }, config, }) - wishlist= wishlistResponse?.wishlist - if(Object.keys(wishlist).length === 0) { - const createWishlistResponse= await config.fetch(createWishlist, {variables: { - wishlistInput: { - customerAccountId: customerId, - name: wishlistName - } - } - }, {headers: { 'x-vol-user-claims': accessToken } }) - wishlist= createWishlistResponse?.data?.createWishlist + wishlist = wishlistResponse?.wishlist + if (Object.keys(wishlist).length === 0) { + const createWishlistResponse = await config.fetch( + createWishlist, + { + variables: { + wishlistInput: { + customerAccountId: customerId, + name: wishlistName, + }, + }, + }, + { headers: { 'x-vol-user-claims': accessToken } } + ) + wishlist = createWishlistResponse?.data?.createWishlist } const productResponse = await config.fetch(getProductQuery, { @@ -103,22 +103,33 @@ const addItem: WishlistEndpoint['handlers']['addItem'] = async ({ const addItemToWishlistResponse = await config.fetch( addItemToWishlistMutation, { - variables: buildAddToWishlistVariables({ ...item, productResponse, wishlist }), + variables: buildAddToWishlistVariables({ + ...item, + productResponse, + wishlist, + }), }, { headers: { 'x-vol-user-claims': accessToken } } ) - if(addItemToWishlistResponse?.data?.createWishlistItem){ - const wishlistResponse= await commerce.getCustomerWishlist({ + if (addItemToWishlistResponse?.data?.createWishlistItem) { + const wishlistResponse = await commerce.getCustomerWishlist({ variables: { customerId, wishlistName }, config, }) - wishlist= wishlistResponse?.wishlist + wishlist = wishlistResponse?.wishlist } - - result = { data: {...wishlist, items: wishlist?.items?.map((item:any) => normalizeWishlistItem(item, config))} } - res.status(200).json({ data: result?.data }) + result = { + data: { + ...wishlist, + items: wishlist?.items?.map((item: any) => + normalizeWishlistItem(item, config) + ), + }, + } + + return { data: result?.data } } export default addItem diff --git a/packages/kibocommerce/src/api/endpoints/wishlist/get-wishlist.ts b/packages/kibocommerce/src/api/endpoints/wishlist/get-wishlist.ts index be4c403d9..070327bf0 100644 --- a/packages/kibocommerce/src/api/endpoints/wishlist/get-wishlist.ts +++ b/packages/kibocommerce/src/api/endpoints/wishlist/get-wishlist.ts @@ -1,35 +1,45 @@ import type { WishlistEndpoint } from '.' + +import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' import getCustomerId from '../../utils/get-customer-id' import { normalizeWishlistItem } from '../../../lib/normalize' // Return wishlist info const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({ - res, body: { customerToken, includeProducts }, config, commerce, }) => { let result: { data?: any } = {} if (customerToken) { - const customerId = customerToken && (await getCustomerId({ customerToken, config })) - const wishlistName= config.defaultWishlistName + const customerId = + customerToken && (await getCustomerId({ customerToken, config })) + const wishlistName = config.defaultWishlistName + if (!customerId) { - // If the customerToken is invalid, then this request is too - return res.status(404).json({ - data: null, - errors: [{ message: 'Wishlist not found' }], + throw new CommerceAPIError('Wishlist not found', { + status: 404, + code: 'not_found', }) } + const { wishlist } = await commerce.getCustomerWishlist({ variables: { customerId, wishlistName }, includeProducts, config, }) - result = { data: {...wishlist, items: wishlist?.items?.map((item:any) => normalizeWishlistItem(item, config, includeProducts))} } + result = { + data: { + ...wishlist, + items: wishlist?.items?.map((item: any) => + normalizeWishlistItem(item, config, includeProducts) + ), + }, + } } - res.status(200).json({ data: result?.data ?? null }) + return { data: result?.data ?? null } } export default getWishlist diff --git a/packages/kibocommerce/src/api/endpoints/wishlist/remove-item.ts b/packages/kibocommerce/src/api/endpoints/wishlist/remove-item.ts index ae6a8d81c..9e9122cfb 100644 --- a/packages/kibocommerce/src/api/endpoints/wishlist/remove-item.ts +++ b/packages/kibocommerce/src/api/endpoints/wishlist/remove-item.ts @@ -1,60 +1,69 @@ -import getCustomerId from '../../utils/get-customer-id' import type { WishlistEndpoint } from '.' +import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' import { normalizeWishlistItem } from '../../../lib/normalize' +import getCustomerId from '../../utils/get-customer-id' import removeItemFromWishlistMutation from '../../mutations/removeItemFromWishlist-mutation' // Return wishlist info const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({ - res, body: { customerToken, itemId }, config, commerce, }) => { - const token = customerToken ? Buffer.from(customerToken, 'base64').toString('ascii'): null; - const accessToken = token ? JSON.parse(token).accessToken : null; + const token = customerToken + ? Buffer.from(customerToken, 'base64').toString('ascii') + : null + const accessToken = token ? JSON.parse(token).accessToken : null let result: { data?: any } = {} let wishlist: any - const customerId = customerToken && (await getCustomerId({ customerToken, config })) - const wishlistName= config.defaultWishlistName + const customerId = + customerToken && (await getCustomerId({ customerToken, config })) + const wishlistName = config.defaultWishlistName const wishlistResponse = await commerce.getCustomerWishlist({ variables: { customerId, wishlistName }, config, }) - wishlist= wishlistResponse?.wishlist - - if (!wishlist || !itemId) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) + wishlist = wishlistResponse?.wishlist + + if (!wishlist) { + throw new CommerceAPIError('Wishlist not found', { status: 404 }) } - const removedItem = wishlist?.items?.find( - (item:any) => { - return item.product.productCode === itemId; - } - ); + + const removedItem = wishlist?.items?.find((item: any) => { + return item.product.productCode === itemId + }) const removeItemFromWishlistResponse = await config.fetch( removeItemFromWishlistMutation, { variables: { wishlistId: wishlist?.id, - wishlistItemId: removedItem?.id + wishlistItemId: removedItem?.id, }, }, { headers: { 'x-vol-user-claims': accessToken } } ) - if(removeItemFromWishlistResponse?.data?.deleteWishlistItem){ - const wishlistResponse= await commerce.getCustomerWishlist({ + if (removeItemFromWishlistResponse?.data?.deleteWishlistItem) { + const wishlistResponse = await commerce.getCustomerWishlist({ variables: { customerId, wishlistName }, config, }) - wishlist= wishlistResponse?.wishlist + wishlist = wishlistResponse?.wishlist + } + result = { + data: { + ...wishlist, + items: wishlist?.items?.map((item: any) => + normalizeWishlistItem(item, config) + ), + }, + } + + return { + data: result?.data, } - result = { data: {...wishlist, items: wishlist?.items?.map((item:any) => normalizeWishlistItem(item, config))} } - res.status(200).json({ data: result?.data }) } export default removeItem diff --git a/packages/kibocommerce/src/api/index.ts b/packages/kibocommerce/src/api/index.ts index aa6d9604e..ebb203ad5 100644 --- a/packages/kibocommerce/src/api/index.ts +++ b/packages/kibocommerce/src/api/index.ts @@ -9,16 +9,15 @@ 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' -import type { RequestInit } from '@vercel/fetch' export interface KiboCommerceConfig extends CommerceAPIConfig { apiHost?: string clientId?: string sharedSecret?: string - customerCookieMaxAgeInDays: number, - currencyCode: string, - documentListName: string, - defaultWishlistName: string, + customerCookieMaxAgeInDays: number + currencyCode: string + documentListName: string + defaultWishlistName: string authUrl?: string } @@ -37,7 +36,7 @@ const config: KiboCommerceConfig = { sharedSecret: process.env.KIBO_SHARED_SECRET || '', customerCookieMaxAgeInDays: 30, currencyCode: 'USD', - defaultWishlistName: 'My Wishlist' + defaultWishlistName: 'My Wishlist', } const operations = { @@ -55,7 +54,7 @@ export const provider = { config, operations } export type KiboCommerceProvider = typeof provider export type KiboCommerceAPI< P extends KiboCommerceProvider = KiboCommerceProvider - > = CommerceAPI

+> = CommerceAPI

export function getCommerceApi

( customProvider: P = provider as any diff --git a/packages/kibocommerce/src/api/utils/api-auth-helper.ts b/packages/kibocommerce/src/api/utils/api-auth-helper.ts index 20cfc7403..a3644d91a 100644 --- a/packages/kibocommerce/src/api/utils/api-auth-helper.ts +++ b/packages/kibocommerce/src/api/utils/api-auth-helper.ts @@ -1,6 +1,4 @@ import type { KiboCommerceConfig } from '../index' -import type { FetchOptions } from '@vercel/fetch' -import fetch from './fetch' // This object is persisted during development const authCache: { kiboAuthTicket?: AppAuthTicket } = {} @@ -41,11 +39,11 @@ export class APIAuthenticationHelper { this._clientId = clientId this._sharedSecret = sharedSecret this._authUrl = authUrl - if(!authTicketCache) { - this._authTicketCache = new RuntimeMemCache(); + if (!authTicketCache) { + this._authTicketCache = new RuntimeMemCache() } } - private _buildFetchOptions(body: any = {}): FetchOptions { + private _buildFetchOptions(body: any = {}): any { return { method: 'POST', headers: { diff --git a/packages/kibocommerce/src/api/utils/cookie-handler.ts b/packages/kibocommerce/src/api/utils/cookie-handler.ts index b4c950fea..7b2fa4f99 100644 --- a/packages/kibocommerce/src/api/utils/cookie-handler.ts +++ b/packages/kibocommerce/src/api/utils/cookie-handler.ts @@ -1,25 +1,25 @@ import { KiboCommerceConfig } from './../index' import { getCookieExpirationDate } from '../../lib/get-cookie-expiration-date' import { prepareSetCookie } from '../../lib/prepare-set-cookie' -import { setCookies } from '../../lib/set-cookie' -import { NextApiRequest } from 'next' + import getAnonymousShopperToken from './get-anonymous-shopper-token' +import type { NextRequest } from 'next/server' const parseCookie = (cookieValue?: any) => { - return cookieValue - ? JSON.parse(Buffer.from(cookieValue, 'base64').toString('ascii')) + return cookieValue + ? JSON.parse(Buffer.from(cookieValue, 'base64').toString('ascii')) : null } export default class CookieHandler { config: KiboCommerceConfig - request: NextApiRequest - response: any + request: NextRequest + headers: HeadersInit | undefined accessToken: any - constructor(config: any, req: NextApiRequest, res: any) { + constructor(config: any, req: NextRequest) { this.config = config this.request = req - this.response = res - const encodedToken = req.cookies[config.customerCookie] + + const encodedToken = req.cookies.get(config.customerCookie) const token = parseCookie(encodedToken) this.accessToken = token ? token.accessToken : null } @@ -36,9 +36,9 @@ export default class CookieHandler { } isShopperCookieAnonymous() { const customerCookieKey = this.config.customerCookie - const shopperCookie = this.request.cookies[customerCookieKey] - const shopperSession = parseCookie(shopperCookie); - const isAnonymous = shopperSession?.customerAccount ? false : true + const shopperCookie = this.request.cookies.get(customerCookieKey) + const shopperSession = parseCookie(shopperCookie) + const isAnonymous = shopperSession?.customerAccount ? false : true return isAnonymous } setAnonymousShopperCookie(anonymousShopperTokenResponse: any) { @@ -53,7 +53,9 @@ export default class CookieHandler { ? { expires: cookieExpirationDate } : {} ) - setCookies(this.response, [authCookie]) + this.headers = { + 'Set-Cookie': authCookie, + } } getAccessToken() { return this.accessToken diff --git a/packages/kibocommerce/src/api/utils/fetch-graphql-api.ts b/packages/kibocommerce/src/api/utils/fetch-graphql-api.ts index cf84f5e41..245e59971 100644 --- a/packages/kibocommerce/src/api/utils/fetch-graphql-api.ts +++ b/packages/kibocommerce/src/api/utils/fetch-graphql-api.ts @@ -1,43 +1,46 @@ import { FetcherError } from '@vercel/commerce/utils/errors' import type { GraphQLFetcher } from '@vercel/commerce/api' import type { KiboCommerceConfig } from '../index' -import fetch from './fetch' -import { APIAuthenticationHelper } from './api-auth-helper'; + +import { APIAuthenticationHelper } from './api-auth-helper' const fetchGraphqlApi: ( getConfig: () => KiboCommerceConfig -) => GraphQLFetcher = (getConfig) => async ( - query: string, - { variables, preview } = {}, - fetchOptions -) => { - const config = getConfig() - const authHelper = new APIAuthenticationHelper(config); - const apiToken = await authHelper.getAccessToken(); - const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { - ...fetchOptions, - method: 'POST', - headers: { - ...fetchOptions?.headers, - Authorization: `Bearer ${apiToken}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query, - variables, - }), - }) - - const json = await res.json() - if (json.errors) { - console.warn(`Kibo API Request Correlation ID: ${res.headers.get('x-vol-correlation')}`); - throw new FetcherError({ - errors: json.errors ?? [{ message: 'Failed to fetch KiboCommerce API' }], - status: res.status, +) => GraphQLFetcher = + (getConfig) => + async (query: string, { variables, preview } = {}, headers?: HeadersInit) => { + const config = getConfig() + const authHelper = new APIAuthenticationHelper(config) + const apiToken = await authHelper.getAccessToken() + const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { + method: 'POST', + headers: { + ...headers, + Authorization: `Bearer ${apiToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query, + variables, + }), }) + + const json = await res.json() + if (json.errors) { + console.warn( + `Kibo API Request Correlation ID: ${res.headers.get( + 'x-vol-correlation' + )}` + ) + throw new FetcherError({ + errors: json.errors ?? [ + { message: 'Failed to fetch KiboCommerce API' }, + ], + status: res.status, + }) + } + + return { data: json.data, res } } - return { data: json.data, res } -} - export default fetchGraphqlApi diff --git a/packages/kibocommerce/src/api/utils/fetch-local.ts b/packages/kibocommerce/src/api/utils/fetch-local.ts index fa3a214cb..43dca7883 100644 --- a/packages/kibocommerce/src/api/utils/fetch-local.ts +++ b/packages/kibocommerce/src/api/utils/fetch-local.ts @@ -1,19 +1,19 @@ import { FetcherError } from '@vercel/commerce/utils/errors' import type { GraphQLFetcher } from '@vercel/commerce/api' import type { KiboCommerceConfig } from '../index' -import fetch from './fetch' -const fetchGraphqlApi: (getConfig: () => KiboCommerceConfig) => GraphQLFetcher = +const fetchGraphqlApi: ( + getConfig: () => KiboCommerceConfig +) => GraphQLFetcher = (getConfig) => - async (query: string, { variables, preview } = {}, fetchOptions) => { + async (query: string, { variables, preview } = {}, headers?: HeadersInit) => { const config = getConfig() const res = await fetch(config.commerceUrl, { - //const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { - ...fetchOptions, + //const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { method: 'POST', headers: { Authorization: `Bearer ${config.apiToken}`, - ...fetchOptions?.headers, + ...headers, 'Content-Type': 'application/json', }, body: JSON.stringify({ @@ -25,7 +25,9 @@ const fetchGraphqlApi: (getConfig: () => KiboCommerceConfig) => GraphQLFetcher = const json = await res.json() if (json.errors) { throw new FetcherError({ - errors: json.errors ?? [{ message: 'Failed to fetch KiboCommerce API' }], + errors: json.errors ?? [ + { message: 'Failed to fetch KiboCommerce API' }, + ], status: res.status, }) } diff --git a/packages/kibocommerce/src/api/utils/get-customer-id.ts b/packages/kibocommerce/src/api/utils/get-customer-id.ts index 5ba3d7787..1d93ade4f 100644 --- a/packages/kibocommerce/src/api/utils/get-customer-id.ts +++ b/packages/kibocommerce/src/api/utils/get-customer-id.ts @@ -8,17 +8,13 @@ async function getCustomerId({ customerToken: string config: KiboCommerceConfig }): Promise { - const token = customerToken ? Buffer.from(customerToken, 'base64').toString('ascii'): null; - const accessToken = token ? JSON.parse(token).accessToken : null; - const { data } = await config.fetch( - getCustomerAccountQuery, - undefined, - { - headers: { - 'x-vol-user-claims': accessToken, - }, - } - ) + const token = customerToken + ? Buffer.from(customerToken, 'base64').toString('ascii') + : null + const accessToken = token ? JSON.parse(token).accessToken : null + const { data } = await config.fetch(getCustomerAccountQuery, undefined, { + 'x-vol-user-claims': accessToken, + }) return data?.customerAccount?.id } diff --git a/packages/kibocommerce/src/auth/use-login.tsx b/packages/kibocommerce/src/auth/use-login.tsx index 324c9d3fe..e85404b5d 100644 --- a/packages/kibocommerce/src/auth/use-login.tsx +++ b/packages/kibocommerce/src/auth/use-login.tsx @@ -10,7 +10,7 @@ export default useLogin as UseLogin export const handler: MutationHook = { fetchOptions: { - url: '/api/login', + url: '/api/commerce/login', method: 'POST', }, async fetcher({ input: { email, password }, options, fetch }) { diff --git a/packages/kibocommerce/src/auth/use-logout.tsx b/packages/kibocommerce/src/auth/use-logout.tsx index ee6c78de7..377180ccc 100644 --- a/packages/kibocommerce/src/auth/use-logout.tsx +++ b/packages/kibocommerce/src/auth/use-logout.tsx @@ -9,7 +9,7 @@ export default useLogout as UseLogout export const handler: MutationHook = { fetchOptions: { - url: '/api/logout', + url: '/api/commerce/logout', method: 'GET', }, useHook: diff --git a/packages/kibocommerce/src/auth/use-signup.tsx b/packages/kibocommerce/src/auth/use-signup.tsx index 893b2beb2..c8f848907 100644 --- a/packages/kibocommerce/src/auth/use-signup.tsx +++ b/packages/kibocommerce/src/auth/use-signup.tsx @@ -9,7 +9,7 @@ export default useSignup as UseSignup export const handler: MutationHook = { fetchOptions: { - url: '/api/signup', + url: '/api/commerce/signup', method: 'POST', }, async fetcher({ diff --git a/packages/kibocommerce/src/cart/use-add-item.tsx b/packages/kibocommerce/src/cart/use-add-item.tsx index bb7b8befc..11189ffeb 100644 --- a/packages/kibocommerce/src/cart/use-add-item.tsx +++ b/packages/kibocommerce/src/cart/use-add-item.tsx @@ -9,7 +9,7 @@ export default useAddItem as UseAddItem export const handler: MutationHook = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'POST', }, async fetcher({ input: item, options, fetch }) { @@ -29,16 +29,18 @@ export const handler: MutationHook = { return data }, - useHook: ({ fetch }) => () => { - const { mutate } = useCart() + useHook: + ({ fetch }) => + () => { + const { mutate } = useCart() - return useCallback( - async function addItem(input) { - const data = await fetch({ input }) - await mutate(data, false) - return data - }, - [fetch, mutate] - ) - }, + return useCallback( + async function addItem(input) { + const data = await fetch({ input }) + await mutate(data, false) + return data + }, + [fetch, mutate] + ) + }, } diff --git a/packages/kibocommerce/src/cart/use-cart.tsx b/packages/kibocommerce/src/cart/use-cart.tsx index 0af0738ec..b8fbede57 100644 --- a/packages/kibocommerce/src/cart/use-cart.tsx +++ b/packages/kibocommerce/src/cart/use-cart.tsx @@ -7,27 +7,29 @@ export default useCart as UseCart export const handler: SWRHook = { fetchOptions: { method: 'GET', - url: '/api/cart', + url: '/api/commerce/cart', }, async fetcher({ options, fetch }) { return await fetch({ ...options }) }, - useHook: ({ useData }) => (input) => { - const response = useData({ - swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, - }) + useHook: + ({ useData }) => + (input) => { + const response = useData({ + swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, + }) - return useMemo( - () => - Object.create(response, { - isEmpty: { - get() { - return (response.data?.lineItems.length ?? 0) <= 0 + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, }, - enumerable: true, - }, - }), - [response] - ) - }, + }), + [response] + ) + }, } diff --git a/packages/kibocommerce/src/cart/use-remove-item.tsx b/packages/kibocommerce/src/cart/use-remove-item.tsx index 3fa883c2f..74f893ccb 100644 --- a/packages/kibocommerce/src/cart/use-remove-item.tsx +++ b/packages/kibocommerce/src/cart/use-remove-item.tsx @@ -4,8 +4,14 @@ import type { HookFetcherContext, } from '@vercel/commerce/utils/types' import { ValidationError } from '@vercel/commerce/utils/errors' -import useRemoveItem, { UseRemoveItem } from '@vercel/commerce/cart/use-remove-item' -import type { Cart, LineItem, RemoveItemHook } from '@vercel/commerce/types/cart' +import useRemoveItem, { + UseRemoveItem, +} from '@vercel/commerce/cart/use-remove-item' +import type { + Cart, + LineItem, + RemoveItemHook, +} from '@vercel/commerce/types/cart' import useCart from './use-cart' export type RemoveItemFn = T extends LineItem @@ -20,7 +26,7 @@ export default useRemoveItem as UseRemoveItem export const handler = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'DELETE', }, async fetcher({ @@ -30,27 +36,25 @@ export const handler = { }: HookFetcherContext) { return await fetch({ ...options, body: { itemId } }) }, - useHook: ({ fetch }: MutationHookContext) => < - T extends LineItem | undefined = undefined - >( - ctx: { item?: T } = {} - ) => { - const { item } = ctx - const { mutate } = useCart() - const removeItem: RemoveItemFn = async (input) => { - const itemId = input?.id ?? item?.id + useHook: + ({ fetch }: MutationHookContext) => + (ctx: { item?: T } = {}) => { + const { item } = ctx + const { mutate } = useCart() + const removeItem: RemoveItemFn = async (input) => { + const itemId = input?.id ?? item?.id - if (!itemId) { - throw new ValidationError({ - message: 'Invalid input used for this operation', - }) + if (!itemId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', + }) + } + + const data = await fetch({ input: { itemId } }) + await mutate(data, false) + return data } - const data = await fetch({ input: { itemId } }) - await mutate(data, false) - return data - } - - return useCallback(removeItem as RemoveItemFn, [fetch, mutate]) - }, + return useCallback(removeItem as RemoveItemFn, [fetch, mutate]) + }, } diff --git a/packages/kibocommerce/src/cart/use-update-item.tsx b/packages/kibocommerce/src/cart/use-update-item.tsx index 931782c4f..f5bfbc410 100644 --- a/packages/kibocommerce/src/cart/use-update-item.tsx +++ b/packages/kibocommerce/src/cart/use-update-item.tsx @@ -5,7 +5,9 @@ import type { HookFetcherContext, } from '@vercel/commerce/utils/types' import { ValidationError } from '@vercel/commerce/utils/errors' -import useUpdateItem, { UseUpdateItem } from '@vercel/commerce/cart/use-update-item' +import useUpdateItem, { + UseUpdateItem, +} from '@vercel/commerce/cart/use-update-item' import type { LineItem, UpdateItemHook } from '@vercel/commerce/types/cart' import { handler as removeItemHandler } from './use-remove-item' import useCart from './use-cart' @@ -18,7 +20,7 @@ export default useUpdateItem as UseUpdateItem export const handler = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'PUT', }, async fetcher({ @@ -46,39 +48,39 @@ export const handler = { body: { itemId, item }, }) }, - useHook: ({ fetch }: MutationHookContext) => < - T extends LineItem | undefined = undefined - >( - ctx: { - item?: T - wait?: number - } = {} - ) => { - const { item } = ctx - const { mutate } = useCart() as any + useHook: + ({ fetch }: MutationHookContext) => + ( + ctx: { + item?: T + wait?: number + } = {} + ) => { + const { item } = ctx + const { mutate } = useCart() as any - return useCallback( - debounce(async (input: UpdateItemActionInput) => { - const itemId = input.id ?? item?.id - const productId = input.productId ?? item?.productId - const variantId = input.productId ?? item?.variantId + return useCallback( + debounce(async (input: UpdateItemActionInput) => { + const itemId = input.id ?? item?.id + const productId = input.productId ?? item?.productId + const variantId = input.productId ?? item?.variantId - if (!itemId || !productId || !variantId) { - throw new ValidationError({ - message: 'Invalid input used for this operation', + if (!itemId || !productId || !variantId) { + throw new ValidationError({ + message: 'Invalid input used for this operation', + }) + } + + const data = await fetch({ + input: { + itemId, + item: { productId, variantId, quantity: input.quantity }, + }, }) - } - - const data = await fetch({ - input: { - itemId, - item: { productId, variantId, quantity: input.quantity }, - }, - }) - await mutate(data, false) - return data - }, ctx.wait ?? 500), - [fetch, mutate] - ) - }, + await mutate(data, false) + return data + }, ctx.wait ?? 500), + [fetch, mutate] + ) + }, } diff --git a/packages/kibocommerce/src/customer/use-customer.tsx b/packages/kibocommerce/src/customer/use-customer.tsx index be13e6cfa..1bd9c5107 100644 --- a/packages/kibocommerce/src/customer/use-customer.tsx +++ b/packages/kibocommerce/src/customer/use-customer.tsx @@ -8,7 +8,7 @@ export default useCustomer as UseCustomer export const handler: SWRHook = { fetchOptions: { - url: '/api/customer', + url: '/api/commerce/customer', method: 'GET', }, async fetcher({ options, fetch }) { diff --git a/packages/kibocommerce/src/lib/set-cookie.ts b/packages/kibocommerce/src/lib/set-cookie.ts index 2c194c921..a76558c20 100644 --- a/packages/kibocommerce/src/lib/set-cookie.ts +++ b/packages/kibocommerce/src/lib/set-cookie.ts @@ -1,3 +1,3 @@ export function setCookies(res: any, cookies: string[]): void { - res.setHeader('Set-Cookie', cookies); -} \ No newline at end of file + res.setHeader('Set-Cookie', cookies) +} diff --git a/packages/kibocommerce/src/product/use-search.tsx b/packages/kibocommerce/src/product/use-search.tsx index 4a9107b4a..ba1ebcf72 100644 --- a/packages/kibocommerce/src/product/use-search.tsx +++ b/packages/kibocommerce/src/product/use-search.tsx @@ -5,7 +5,7 @@ export default useSearch as UseSearch export const handler: SWRHook = { fetchOptions: { method: 'GET', - url: '/api/catalog/products', + url: '/api/commerce/catalog/products', }, fetcher({ input: { search, categoryId, brandId, sort }, options, fetch }) { // Use a dummy base as we only care about the relative path @@ -23,15 +23,17 @@ export const handler: SWRHook = { method: options.method, }) }, - useHook: ({ useData }) => (input) => { - return useData({ - input: [ - ['search', input.search], - ['categoryId', input.categoryId], - ['brandId', input.brandId], - ['sort', input.sort], - ], - swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, - }) - }, + useHook: + ({ useData }) => + (input) => { + return useData({ + input: [ + ['search', input.search], + ['categoryId', input.categoryId], + ['brandId', input.brandId], + ['sort', input.sort], + ], + swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, + }) + }, } diff --git a/packages/kibocommerce/src/wishlist/use-add-item.tsx b/packages/kibocommerce/src/wishlist/use-add-item.tsx index 3f634e31e..6955778e5 100644 --- a/packages/kibocommerce/src/wishlist/use-add-item.tsx +++ b/packages/kibocommerce/src/wishlist/use-add-item.tsx @@ -10,7 +10,7 @@ export default useAddItem as UseAddItem export const handler: MutationHook = { fetchOptions: { - url: '/api/wishlist', + url: '/api/commerce/wishlist', method: 'POST', }, useHook: diff --git a/packages/kibocommerce/src/wishlist/use-remove-item.tsx b/packages/kibocommerce/src/wishlist/use-remove-item.tsx index 490154a45..e61328769 100644 --- a/packages/kibocommerce/src/wishlist/use-remove-item.tsx +++ b/packages/kibocommerce/src/wishlist/use-remove-item.tsx @@ -12,7 +12,7 @@ export default useRemoveItem as UseRemoveItem export const handler: MutationHook = { fetchOptions: { - url: '/api/wishlist', + url: '/api/commerce/wishlist', method: 'DELETE', }, useHook: diff --git a/packages/kibocommerce/src/wishlist/use-wishlist.tsx b/packages/kibocommerce/src/wishlist/use-wishlist.tsx index a91eb419b..83d3fadfc 100644 --- a/packages/kibocommerce/src/wishlist/use-wishlist.tsx +++ b/packages/kibocommerce/src/wishlist/use-wishlist.tsx @@ -1,6 +1,8 @@ import { useMemo } from 'react' import { SWRHook } from '@vercel/commerce/utils/types' -import useWishlist, { UseWishlist } from '@vercel/commerce/wishlist/use-wishlist' +import useWishlist, { + UseWishlist, +} from '@vercel/commerce/wishlist/use-wishlist' import type { GetWishlistHook } from '@vercel/commerce/types/wishlist' import useCustomer from '../customer/use-customer' @@ -8,45 +10,47 @@ export default useWishlist as UseWishlist export const handler: SWRHook = { fetchOptions: { - url: '/api/wishlist', + url: '/api/commerce/wishlist', method: 'GET', }, - fetcher({ input: { customerId, includeProducts}, options, fetch }) { + fetcher({ input: { customerId, includeProducts }, options, fetch }) { if (!customerId) return null // Use a dummy base as we only care about the relative path const url = new URL(options.url!, 'http://a') if (includeProducts) url.searchParams.set('products', '1') - if(customerId) url.searchParams.set('customerId', customerId) + if (customerId) url.searchParams.set('customerId', customerId) return fetch({ url: url.pathname + url.search, method: options.method, }) }, - useHook: ({ useData }) => (input) => { - const { data: customer } = useCustomer() - const response = useData({ - input: [ - ['customerId', customer?.id], - ['includeProducts', input?.includeProducts], - ], - swrOptions: { - revalidateOnFocus: false, - ...input?.swrOptions, - }, - }) - return useMemo( - () => - Object.create(response, { - isEmpty: { - get() { - return (response.data?.items?.length || 0) <= 0 + useHook: + ({ useData }) => + (input) => { + const { data: customer } = useCustomer() + const response = useData({ + input: [ + ['customerId', customer?.id], + ['includeProducts', input?.includeProducts], + ], + swrOptions: { + revalidateOnFocus: false, + ...input?.swrOptions, + }, + }) + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.items?.length || 0) <= 0 + }, + enumerable: true, }, - enumerable: true, - }, - }), - [response] - ) - }, + }), + [response] + ) + }, } diff --git a/packages/local/package.json b/packages/local/package.json index 8e0ed6f29..a62cedb6d 100644 --- a/packages/local/package.json +++ b/packages/local/package.json @@ -47,9 +47,7 @@ } }, "dependencies": { - "@vercel/commerce": "workspace:*", - "@vercel/fetch": "^6.2.0", - "node-fetch": "^2.6.7" + "@vercel/commerce": "workspace:*" }, "peerDependencies": { "next": "^12", diff --git a/packages/local/src/api/endpoints.ts b/packages/local/src/api/endpoints.ts new file mode 100644 index 000000000..b921d5e4a --- /dev/null +++ b/packages/local/src/api/endpoints.ts @@ -0,0 +1,8 @@ +import createEndpoints from '@vercel/commerce/api/endpoints' +import type { LocalAPI } from '.' + +const endpoints = {} + +export default function localAPI(commerce: LocalAPI) { + return createEndpoints(commerce, endpoints) +} diff --git a/packages/local/src/api/endpoints/cart/index.ts b/packages/local/src/api/endpoints/cart/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/local/src/api/endpoints/cart/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/local/src/api/endpoints/catalog/index.ts b/packages/local/src/api/endpoints/catalog/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/local/src/api/endpoints/catalog/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/local/src/api/endpoints/catalog/products.ts b/packages/local/src/api/endpoints/catalog/products.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/local/src/api/endpoints/catalog/products.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/local/src/api/endpoints/checkout/index.ts b/packages/local/src/api/endpoints/checkout/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/local/src/api/endpoints/checkout/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/local/src/api/endpoints/customer/address.ts b/packages/local/src/api/endpoints/customer/address.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/local/src/api/endpoints/customer/address.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/local/src/api/endpoints/customer/card.ts b/packages/local/src/api/endpoints/customer/card.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/local/src/api/endpoints/customer/card.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/local/src/api/endpoints/customer/index.ts b/packages/local/src/api/endpoints/customer/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/local/src/api/endpoints/customer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/local/src/api/endpoints/login/index.ts b/packages/local/src/api/endpoints/login/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/local/src/api/endpoints/login/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/local/src/api/endpoints/logout/index.ts b/packages/local/src/api/endpoints/logout/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/local/src/api/endpoints/logout/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/local/src/api/endpoints/signup/index.ts b/packages/local/src/api/endpoints/signup/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/local/src/api/endpoints/signup/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/local/src/api/endpoints/wishlist/index.tsx b/packages/local/src/api/endpoints/wishlist/index.tsx deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/local/src/api/endpoints/wishlist/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/local/src/api/utils/fetch-local.ts b/packages/local/src/api/utils/fetch-local.ts index ae84fff8b..624f1d288 100644 --- a/packages/local/src/api/utils/fetch-local.ts +++ b/packages/local/src/api/utils/fetch-local.ts @@ -1,17 +1,15 @@ import { FetcherError } from '@vercel/commerce/utils/errors' import type { GraphQLFetcher } from '@vercel/commerce/api' import type { LocalConfig } from '../index' -import fetch from './fetch' const fetchGraphqlApi: (getConfig: () => LocalConfig) => GraphQLFetcher = (getConfig) => - async (query: string, { variables, preview } = {}, fetchOptions) => { + async (query: string, { variables, preview } = {}, headers?: HeadersInit) => { const config = getConfig() const res = await fetch(config.commerceUrl, { - ...fetchOptions, method: 'POST', headers: { - ...fetchOptions?.headers, + ...headers, 'Content-Type': 'application/json', }, body: JSON.stringify({ diff --git a/packages/ordercloud/package.json b/packages/ordercloud/package.json index da9384071..7991fd955 100644 --- a/packages/ordercloud/package.json +++ b/packages/ordercloud/package.json @@ -48,10 +48,7 @@ }, "dependencies": { "@vercel/commerce": "workspace:*", - "@vercel/fetch": "^6.2.0", - "stripe": "^8.197.0", "lodash.debounce": "^4.0.8", - "node-fetch": "^2.6.7", "cookie": "^0.4.1" }, "peerDependencies": { diff --git a/packages/ordercloud/src/api/endpoints/cart/add-item.ts b/packages/ordercloud/src/api/endpoints/cart/add-item.ts index 8894d921d..bed470602 100644 --- a/packages/ordercloud/src/api/endpoints/cart/add-item.ts +++ b/packages/ordercloud/src/api/endpoints/cart/add-item.ts @@ -1,72 +1,65 @@ import type { CartEndpoint } from '.' -import type { RawVariant } from '../../../types/product' -import type { LineItem } from '@vercel/commerce/types/cart' - -import { serialize } from 'cookie' +import type { RawVariantSpec } from '../../../types/product' import { formatCart } from '../../utils/cart' +import { serialize } from 'cookie' const addItem: CartEndpoint['handlers']['addItem'] = async ({ - res, + req, body: { cartId, item }, config: { restBuyerFetch, cartCookie, tokenCookie }, }) => { - // Return an error if no item is present - if (!item) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Missing item' }], - }) - } - - // Store token - let token - - // Set the quantity if not present - if (!item.quantity) item.quantity = 1 + // Get token + let token = req.cookies.get(tokenCookie) + let headers: any = {} // Create an order if it doesn't exist if (!cartId) { const { ID, meta } = await restBuyerFetch( 'POST', `/orders/Outgoing`, - {} - ).then((response: { ID: string; meta: { token: string } }) => response) + {}, + { token } + ) - // Set the cart id and token cartId = ID - token = meta.token - // Set the cart and token cookie - res.setHeader('Set-Cookie', [ - serialize(tokenCookie, meta.token, { - maxAge: 60 * 60 * 24 * 30, - expires: new Date(Date.now() + 60 * 60 * 24 * 30 * 1000), - secure: process.env.NODE_ENV === 'production', - path: '/', - sameSite: 'lax', - }), - serialize(cartCookie, cartId, { - maxAge: 60 * 60 * 24 * 30, - expires: new Date(Date.now() + 60 * 60 * 24 * 30 * 1000), - secure: process.env.NODE_ENV === 'production', - path: '/', - sameSite: 'lax', - }), - ]) + headers = { + 'set-cookie': [ + serialize(cartCookie, cartId!, { + maxAge: 60 * 60 * 24 * 30, + expires: new Date(Date.now() + 60 * 60 * 24 * 30 * 1000), + secure: process.env.NODE_ENV === 'production', + path: '/', + sameSite: 'lax', + }), + ], + } + + if (meta?.token) { + headers['set-cookie'].push( + serialize(tokenCookie, meta.token?.access_token, { + maxAge: meta.token.expires_in, + expires: new Date(Date.now() + meta.token.expires_in * 1000), + secure: process.env.NODE_ENV === 'production', + path: '/', + sameSite: 'lax', + }) + ) + } } - // Store specs - let specs: RawVariant['Specs'] = [] + let specs: RawVariantSpec[] = [] // If a variant is present, fetch its specs - if (item.variantId) { - specs = await restBuyerFetch( + if (item.variantId !== 'undefined') { + const { Specs } = await restBuyerFetch( 'GET', `/me/products/${item.productId}/variants/${item.variantId}`, null, { token } - ).then((res: RawVariant) => res.Specs) + ) + specs = Specs } // Add the item to the order @@ -81,19 +74,19 @@ const addItem: CartEndpoint['handlers']['addItem'] = async ({ { token } ) - // Get cart - const [cart, lineItems] = await Promise.all([ + // Get cart & line items + const [cart, { Items }] = await Promise.all([ restBuyerFetch('GET', `/orders/Outgoing/${cartId}`, null, { token }), restBuyerFetch('GET', `/orders/Outgoing/${cartId}/lineitems`, null, { token, - }).then((response: { Items: LineItem[] }) => response.Items), + }), ]) // Format cart - const formattedCart = formatCart(cart, lineItems) + const formattedCart = formatCart(cart, Items) - // Return cart and errors - res.status(200).json({ data: formattedCart, errors: [] }) + // Return cart and headers + return { data: formattedCart, headers } } export default addItem diff --git a/packages/ordercloud/src/api/endpoints/cart/get-cart.ts b/packages/ordercloud/src/api/endpoints/cart/get-cart.ts index 7ea077b54..7cb23cb26 100644 --- a/packages/ordercloud/src/api/endpoints/cart/get-cart.ts +++ b/packages/ordercloud/src/api/endpoints/cart/get-cart.ts @@ -1,64 +1,56 @@ -import type { OrdercloudLineItem } from '../../../types/cart' import type { CartEndpoint } from '.' import { serialize } from 'cookie' - import { formatCart } from '../../utils/cart' // Return current cart info const getCart: CartEndpoint['handlers']['getCart'] = async ({ req, - res, body: { cartId }, config: { restBuyerFetch, cartCookie, tokenCookie }, }) => { + // If no cartId is provided, return data null if (!cartId) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) + return { data: null } } try { - // Get token from cookies - const token = req.cookies[tokenCookie] + // Get token + const token = req.cookies.get(tokenCookie) - // Get cart - const cart = await restBuyerFetch( - 'GET', - `/orders/Outgoing/${cartId}`, - null, - { token } - ) - - // Get line items - const lineItems = await restBuyerFetch( - 'GET', - `/orders/Outgoing/${cartId}/lineitems`, - null, - { token } - ).then((response: { Items: OrdercloudLineItem[] }) => response.Items) - - // Format cart - const formattedCart = formatCart(cart, lineItems) - - // Return cart and errors - res.status(200).json({ data: formattedCart, errors: [] }) - } catch (error) { - // Reset cart and token cookie - res.setHeader('Set-Cookie', [ - serialize(cartCookie, cartId, { - maxAge: -1, - path: '/', - }), - serialize(tokenCookie, cartId, { - maxAge: -1, - path: '/', + // Get cart & line items + const [cart, { Items }] = await Promise.all([ + restBuyerFetch('GET', `/orders/Outgoing/${cartId}`, null, { token }), + restBuyerFetch('GET', `/orders/Outgoing/${cartId}/lineitems`, null, { + token, }), ]) + // Format cart + const formattedCart = formatCart(cart, Items) + // Return cart and errors + return { + data: formattedCart, + } + } catch (error) { + console.error(error) + const headers = { + 'set-cookie': [ + serialize(cartCookie, '', { + maxAge: -1, + path: '/', + }), + serialize(tokenCookie, '', { + maxAge: -1, + path: '/', + }), + ], + } // Return empty cart - res.status(200).json({ data: null, errors: [] }) + return { + data: null, + headers, + } } } diff --git a/packages/ordercloud/src/api/endpoints/cart/index.ts b/packages/ordercloud/src/api/endpoints/cart/index.ts index e458964f9..ad1c40495 100644 --- a/packages/ordercloud/src/api/endpoints/cart/index.ts +++ b/packages/ordercloud/src/api/endpoints/cart/index.ts @@ -1,5 +1,5 @@ -import type { CartSchema } from '@vercel/commerce/types/cart' import type { OrdercloudAPI } from '../..' +import type { CartSchema } from '@vercel/commerce/types/cart' import { GetAPISchema, createEndpoint } from '@vercel/commerce/api' import cartEndpoint from '@vercel/commerce/api/endpoints/cart' @@ -9,9 +9,8 @@ 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 type CartAPI = GetAPISchema export const handlers: CartEndpoint['handlers'] = { getCart, diff --git a/packages/ordercloud/src/api/endpoints/cart/remove-item.ts b/packages/ordercloud/src/api/endpoints/cart/remove-item.ts index ea9c46e4c..d3633b485 100644 --- a/packages/ordercloud/src/api/endpoints/cart/remove-item.ts +++ b/packages/ordercloud/src/api/endpoints/cart/remove-item.ts @@ -1,30 +1,22 @@ import type { CartEndpoint } from '.' - import { formatCart } from '../../utils/cart' import { OrdercloudLineItem } from '../../../types/cart' const removeItem: CartEndpoint['handlers']['removeItem'] = async ({ req, - res, body: { cartId, itemId }, config: { restBuyerFetch, tokenCookie }, }) => { - if (!cartId || !itemId) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) - } - - // Get token from cookies - const token = req.cookies[tokenCookie] + const token = req.cookies.get(tokenCookie) // Remove the item to the order await restBuyerFetch( 'DELETE', `/orders/Outgoing/${cartId}/lineitems/${itemId}`, null, - { token } + { + token, + } ) // Get cart @@ -39,7 +31,7 @@ const removeItem: CartEndpoint['handlers']['removeItem'] = async ({ const formattedCart = formatCart(cart, lineItems) // Return cart and errors - res.status(200).json({ data: formattedCart, errors: [] }) + return { data: formattedCart } } export default removeItem diff --git a/packages/ordercloud/src/api/endpoints/cart/update-item.ts b/packages/ordercloud/src/api/endpoints/cart/update-item.ts index 20113baee..543270d22 100644 --- a/packages/ordercloud/src/api/endpoints/cart/update-item.ts +++ b/packages/ordercloud/src/api/endpoints/cart/update-item.ts @@ -6,19 +6,10 @@ import { formatCart } from '../../utils/cart' const updateItem: CartEndpoint['handlers']['updateItem'] = async ({ req, - res, body: { cartId, itemId, item }, config: { restBuyerFetch, tokenCookie }, }) => { - if (!cartId || !itemId || !item) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) - } - - // Get token from cookies - const token = req.cookies[tokenCookie] + const token = req.cookies.get(tokenCookie) // Store specs let specs: RawVariant['Specs'] = [] @@ -27,9 +18,7 @@ const updateItem: CartEndpoint['handlers']['updateItem'] = async ({ if (item.variantId) { specs = await restBuyerFetch( 'GET', - `/me/products/${item.productId}/variants/${item.variantId}`, - null, - { token } + `/me/products/${item.productId}/variants/${item.variantId}` ).then((res: RawVariant) => res.Specs) } @@ -42,7 +31,9 @@ const updateItem: CartEndpoint['handlers']['updateItem'] = async ({ Quantity: item.quantity, Specs: specs, }, - { token } + { + token, + } ) // Get cart @@ -57,7 +48,7 @@ const updateItem: CartEndpoint['handlers']['updateItem'] = async ({ const formattedCart = formatCart(cart, lineItems) // Return cart and errors - res.status(200).json({ data: formattedCart, errors: [] }) + return { data: formattedCart } } export default updateItem diff --git a/packages/ordercloud/src/api/endpoints/catalog/products/get-products.ts b/packages/ordercloud/src/api/endpoints/catalog/products/get-products.ts index ad8d391ac..1ccadc445 100644 --- a/packages/ordercloud/src/api/endpoints/catalog/products/get-products.ts +++ b/packages/ordercloud/src/api/endpoints/catalog/products/get-products.ts @@ -1,13 +1,14 @@ -import { normalize as normalizeProduct } from '../../../../utils/product' import { ProductsEndpoint } from '.' +import { normalize as normalizeProduct } from '../../../../utils/product' // Get products for the product list page. Search and category filter implemented. Sort and brand filter not implemented. const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({ req, - res, - body: { search, categoryId, brandId, sort }, - config: { restBuyerFetch, cartCookie, tokenCookie }, + body: { search, categoryId }, + config: { restBuyerFetch, tokenCookie }, }) => { + const token = req.cookies.get(tokenCookie) + //Use a dummy base as we only care about the relative path const url = new URL('/me/products', 'http://a') @@ -18,20 +19,19 @@ const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({ url.searchParams.set('categoryID', String(categoryId)) } - // Get token from cookies - const token = req.cookies[tokenCookie] - var rawProducts = await restBuyerFetch( 'GET', url.pathname + url.search, null, { token } - ) + ).then((response: { Items: any[] }) => response.Items) - const products = rawProducts.Items.map(normalizeProduct) - const found = rawProducts?.Items?.length > 0 - - res.status(200).json({ data: { products, found } }) + return { + data: { + products: rawProducts.map(normalizeProduct), + found: rawProducts?.length > 0, + }, + } } export default getProducts diff --git a/packages/ordercloud/src/api/endpoints/checkout/get-checkout.ts b/packages/ordercloud/src/api/endpoints/checkout/get-checkout.ts index eacf88e83..6919f2a01 100644 --- a/packages/ordercloud/src/api/endpoints/checkout/get-checkout.ts +++ b/packages/ordercloud/src/api/endpoints/checkout/get-checkout.ts @@ -2,27 +2,15 @@ import type { CheckoutEndpoint } from '.' const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ req, - res, body: { cartId }, - config: { restBuyerFetch, tokenCookie }, + config: { restBuyerFetch }, }) => { - // Return an error if no item is present - if (!cartId) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Missing cookie' }], - }) - } - - // Get token from cookies - const token = req.cookies[tokenCookie] + const token = req.cookies.get('token') // Register credit card const payments = await restBuyerFetch( 'GET', - `/orders/Outgoing/${cartId}/payments`, - null, - { token } + `/orders/Outgoing/${cartId}/payments` ).then((response: { Items: unknown[] }) => response.Items) const address = await restBuyerFetch( @@ -35,15 +23,15 @@ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ ) // Return cart and errors - res.status(200).json({ + + return { data: { hasPayment: payments.length > 0, hasShipping: Boolean(address), addressId: address, cardId: payments[0]?.ID, }, - errors: [], - }) + } } export default getCheckout diff --git a/packages/ordercloud/src/api/endpoints/checkout/submit-checkout.ts b/packages/ordercloud/src/api/endpoints/checkout/submit-checkout.ts index 8cd9be5e4..03316f843 100644 --- a/packages/ordercloud/src/api/endpoints/checkout/submit-checkout.ts +++ b/packages/ordercloud/src/api/endpoints/checkout/submit-checkout.ts @@ -2,31 +2,18 @@ import type { CheckoutEndpoint } from '.' const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({ req, - res, body: { cartId }, config: { restBuyerFetch, tokenCookie }, }) => { - // Return an error if no item is present - if (!cartId) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Missing item' }], - }) - } - - // Get token from cookies - const token = req.cookies[tokenCookie] + const token = req.cookies.get(tokenCookie) // Submit order - await restBuyerFetch( - 'POST', - `/orders/Outgoing/${cartId}/submit`, - {}, - { token } - ) + await restBuyerFetch('POST', `/orders/Outgoing/${cartId}/submit`, null, { + token, + }) // Return cart and errors - res.status(200).json({ data: null, errors: [] }) + return { data: null } } export default submitCheckout diff --git a/packages/ordercloud/src/api/endpoints/customer/address/add-item.ts b/packages/ordercloud/src/api/endpoints/customer/address/add-item.ts index 434c2400d..951a8a74c 100644 --- a/packages/ordercloud/src/api/endpoints/customer/address/add-item.ts +++ b/packages/ordercloud/src/api/endpoints/customer/address/add-item.ts @@ -1,25 +1,11 @@ import type { CustomerAddressEndpoint } from '.' const addItem: CustomerAddressEndpoint['handlers']['addItem'] = async ({ - res, + req, body: { item, cartId }, - config: { restBuyerFetch }, + config: { restBuyerFetch, tokenCookie }, }) => { - // Return an error if no item is present - if (!item) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Missing item' }], - }) - } - - // Return an error if no item is present - if (!cartId) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Cookie not found' }], - }) - } + const token = req.cookies.get(tokenCookie) // Register address const address = await restBuyerFetch('POST', `/me/addresses`, { @@ -37,11 +23,16 @@ const addItem: CustomerAddressEndpoint['handlers']['addItem'] = async ({ }).then((response: { ID: string }) => response.ID) // Assign address to order - await restBuyerFetch('PATCH', `/orders/Outgoing/${cartId}`, { - ShippingAddressID: address, - }) + await restBuyerFetch( + 'PATCH', + `/orders/Outgoing/${cartId}`, + { + ShippingAddressID: address, + }, + { token } + ) - return res.status(200).json({ data: null, errors: [] }) + return { data: null } } export default addItem diff --git a/packages/ordercloud/src/api/endpoints/customer/address/get-addresses.ts b/packages/ordercloud/src/api/endpoints/customer/address/get-addresses.ts index 2e27591c0..27c8e7d7b 100644 --- a/packages/ordercloud/src/api/endpoints/customer/address/get-addresses.ts +++ b/packages/ordercloud/src/api/endpoints/customer/address/get-addresses.ts @@ -1,9 +1,8 @@ import type { CustomerAddressEndpoint } from '.' -const getCards: CustomerAddressEndpoint['handlers']['getAddresses'] = async ({ - res, -}) => { - return res.status(200).json({ data: null, errors: [] }) -} +const getAddresses: CustomerAddressEndpoint['handlers']['getAddresses'] = + () => { + return Promise.resolve({ data: null }) + } -export default getCards +export default getAddresses diff --git a/packages/ordercloud/src/api/endpoints/customer/address/remove-item.ts b/packages/ordercloud/src/api/endpoints/customer/address/remove-item.ts index fba4e1154..14e05ff41 100644 --- a/packages/ordercloud/src/api/endpoints/customer/address/remove-item.ts +++ b/packages/ordercloud/src/api/endpoints/customer/address/remove-item.ts @@ -1,9 +1,7 @@ import type { CustomerAddressEndpoint } from '.' -const removeItem: CustomerAddressEndpoint['handlers']['removeItem'] = async ({ - res, -}) => { - return res.status(200).json({ data: null, errors: [] }) +const removeItem: CustomerAddressEndpoint['handlers']['removeItem'] = () => { + return Promise.resolve({ data: null }) } export default removeItem diff --git a/packages/ordercloud/src/api/endpoints/customer/address/update-item.ts b/packages/ordercloud/src/api/endpoints/customer/address/update-item.ts index 4c4b4b9ae..74e2c3bec 100644 --- a/packages/ordercloud/src/api/endpoints/customer/address/update-item.ts +++ b/packages/ordercloud/src/api/endpoints/customer/address/update-item.ts @@ -1,9 +1,7 @@ import type { CustomerAddressEndpoint } from '.' -const updateItem: CustomerAddressEndpoint['handlers']['updateItem'] = async ({ - res, -}) => { - return res.status(200).json({ data: null, errors: [] }) +const updateItem: CustomerAddressEndpoint['handlers']['updateItem'] = () => { + return Promise.resolve({ data: null }) } export default updateItem diff --git a/packages/ordercloud/src/api/endpoints/customer/card/add-item.ts b/packages/ordercloud/src/api/endpoints/customer/card/add-item.ts index ad7dead7c..0b6ced31d 100644 --- a/packages/ordercloud/src/api/endpoints/customer/card/add-item.ts +++ b/packages/ordercloud/src/api/endpoints/customer/card/add-item.ts @@ -1,53 +1,47 @@ import type { CustomerCardEndpoint } from '.' import type { OredercloudCreditCard } from '../../../../types/customer/card' -import Stripe from 'stripe' - -const stripe = new Stripe(process.env.STRIPE_SECRET as string, { - apiVersion: '2020-08-27', -}) - const addItem: CustomerCardEndpoint['handlers']['addItem'] = async ({ - res, + req, body: { item, cartId }, - config: { restBuyerFetch, restMiddlewareFetch }, + config: { restBuyerFetch, tokenCookie }, }) => { - // Return an error if no item is present - if (!item) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Missing item' }], - }) - } - - // Return an error if no item is present - if (!cartId) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Cookie not found' }], - }) - } - // Get token - const token = await stripe.tokens - .create({ + const token = req.cookies.get(tokenCookie) + + const [exp_month, exp_year] = item.cardExpireDate.split('/') + const stripeToken = await fetch('https://api.stripe.com/v1/tokens', { + method: 'POST', + headers: { + Authorization: `Bearer ${process.env.STRIPE_SECRET}`, + }, + body: JSON.stringify({ card: { number: item.cardNumber, - exp_month: item.cardExpireDate.split('/')[0], - exp_year: item.cardExpireDate.split('/')[1], + exp_month, + exp_year, cvc: item.cardCvc, }, - }) - .then((res: { id: string }) => res.id) + }), + }) + .then((res) => res.json()) + .then((res) => res.id) // Register credit card - const creditCard = await restBuyerFetch('POST', `/me/creditcards`, { - Token: token, - CardType: 'credit', - PartialAccountNumber: item.cardNumber.slice(-4), - CardholderName: item.cardHolder, - ExpirationDate: item.cardExpireDate, - }).then((response: OredercloudCreditCard) => response.ID) + const creditCard = await restBuyerFetch( + 'POST', + `/me/creditcards`, + { + Token: stripeToken, + CardType: 'credit', + PartialAccountNumber: item.cardNumber.slice(-4), + CardholderName: item.cardHolder, + ExpirationDate: item.cardExpireDate, + }, + { + token, + } + ).then((response: OredercloudCreditCard) => response.ID) // Assign payment to order const payment = await restBuyerFetch( @@ -56,19 +50,18 @@ const addItem: CustomerCardEndpoint['handlers']['addItem'] = async ({ { Type: 'CreditCard', CreditCardID: creditCard, + }, + { + token, } ).then((response: { ID: string }) => response.ID) // Accept payment to order - await restMiddlewareFetch( - 'PATCH', - `/orders/All/${cartId}/payments/${payment}`, - { - Accepted: true, - } - ) + await restBuyerFetch('PATCH', `/orders/All/${cartId}/payments/${payment}`, { + Accepted: true, + }) - return res.status(200).json({ data: null, errors: [] }) + return { data: null } } export default addItem diff --git a/packages/ordercloud/src/api/endpoints/customer/card/get-cards.ts b/packages/ordercloud/src/api/endpoints/customer/card/get-cards.ts index e77520803..169dbfdc5 100644 --- a/packages/ordercloud/src/api/endpoints/customer/card/get-cards.ts +++ b/packages/ordercloud/src/api/endpoints/customer/card/get-cards.ts @@ -1,9 +1,7 @@ import type { CustomerCardEndpoint } from '.' -const getCards: CustomerCardEndpoint['handlers']['getCards'] = async ({ - res, -}) => { - return res.status(200).json({ data: null, errors: [] }) +const getCards: CustomerCardEndpoint['handlers']['getCards'] = () => { + return Promise.resolve({ data: null }) } export default getCards diff --git a/packages/ordercloud/src/api/endpoints/customer/card/remove-item.ts b/packages/ordercloud/src/api/endpoints/customer/card/remove-item.ts index 1a81d1cf4..1d7038c71 100644 --- a/packages/ordercloud/src/api/endpoints/customer/card/remove-item.ts +++ b/packages/ordercloud/src/api/endpoints/customer/card/remove-item.ts @@ -1,9 +1,7 @@ import type { CustomerCardEndpoint } from '.' -const removeItem: CustomerCardEndpoint['handlers']['removeItem'] = async ({ - res, -}) => { - return res.status(200).json({ data: null, errors: [] }) +const removeItem: CustomerCardEndpoint['handlers']['removeItem'] = () => { + return Promise.resolve({ data: null }) } export default removeItem diff --git a/packages/ordercloud/src/api/endpoints/customer/card/update-item.ts b/packages/ordercloud/src/api/endpoints/customer/card/update-item.ts index 9770644aa..dbf3f5ed0 100644 --- a/packages/ordercloud/src/api/endpoints/customer/card/update-item.ts +++ b/packages/ordercloud/src/api/endpoints/customer/card/update-item.ts @@ -1,9 +1,7 @@ import type { CustomerCardEndpoint } from '.' -const updateItem: CustomerCardEndpoint['handlers']['updateItem'] = async ({ - res, -}) => { - return res.status(200).json({ data: null, errors: [] }) +const updateItem: CustomerCardEndpoint['handlers']['updateItem'] = () => { + return Promise.resolve({ data: null }) } export default updateItem diff --git a/packages/ordercloud/src/api/endpoints/customer/index.ts b/packages/ordercloud/src/api/endpoints/customer/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/ordercloud/src/api/endpoints/customer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/ordercloud/src/api/endpoints/index.ts b/packages/ordercloud/src/api/endpoints/index.ts new file mode 100644 index 000000000..5ea4659d0 --- /dev/null +++ b/packages/ordercloud/src/api/endpoints/index.ts @@ -0,0 +1,21 @@ +import type { OrdercloudAPI } from '..' + +import createEndpoints from '@vercel/commerce/api/endpoints' + +import cart from './cart' +import checkout from './checkout' +import products from './catalog/products' +import customerCard from './customer/card' +import customerAddress from './customer/address' + +const endpoints = { + cart, + checkout, + 'customer/card': customerCard, + 'customer/address': customerAddress, + 'catalog/products': products, +} + +export default function ordercloudAPI(commerce: OrdercloudAPI) { + return createEndpoints(commerce, endpoints) +} diff --git a/packages/ordercloud/src/api/endpoints/login/index.ts b/packages/ordercloud/src/api/endpoints/login/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/ordercloud/src/api/endpoints/login/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/ordercloud/src/api/endpoints/logout/index.ts b/packages/ordercloud/src/api/endpoints/logout/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/ordercloud/src/api/endpoints/logout/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/ordercloud/src/api/endpoints/signup/index.ts b/packages/ordercloud/src/api/endpoints/signup/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/ordercloud/src/api/endpoints/signup/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/ordercloud/src/api/endpoints/wishlist/index.tsx b/packages/ordercloud/src/api/endpoints/wishlist/index.tsx deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/ordercloud/src/api/endpoints/wishlist/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/ordercloud/src/api/index.ts b/packages/ordercloud/src/api/index.ts index d1a809eca..01197d549 100644 --- a/packages/ordercloud/src/api/index.ts +++ b/packages/ordercloud/src/api/index.ts @@ -1,6 +1,6 @@ import type { CommerceAPI, CommerceAPIConfig } from '@vercel/commerce/api' import { getCommerceApi as commerceApi } from '@vercel/commerce/api' -import { createBuyerFetcher, createMiddlewareFetcher } from './utils/fetch-rest' +import { createBuyerFetcher } from './utils/fetch-rest' import createGraphqlFetcher from './utils/fetch-graphql' import getAllPages from './operations/get-all-pages' @@ -25,12 +25,6 @@ export interface OrdercloudConfig extends CommerceAPIConfig { body?: Record, fetchOptions?: Record ) => Promise - restMiddlewareFetch: ( - method: string, - resource: string, - body?: Record, - fetchOptions?: Record - ) => Promise apiVersion: string tokenCookie: string } @@ -44,9 +38,6 @@ const config: OrdercloudConfig = { tokenCookie: TOKEN_COOKIE, cartCookieMaxAge: 2592000, restBuyerFetch: createBuyerFetcher(() => getCommerceApi().getConfig()), - restMiddlewareFetch: createMiddlewareFetcher(() => - getCommerceApi().getConfig() - ), fetch: createGraphqlFetcher(() => getCommerceApi().getConfig()), } diff --git a/packages/ordercloud/src/api/utils/cart.ts b/packages/ordercloud/src/api/utils/cart.ts index fd0c79bf3..473387652 100644 --- a/packages/ordercloud/src/api/utils/cart.ts +++ b/packages/ordercloud/src/api/utils/cart.ts @@ -21,7 +21,7 @@ export function formatCart( name: lineItem.Product.Name, quantity: lineItem.Quantity, discounts: [], - path: lineItem.ProductID, + path: `/product/${lineItem.ProductID}`, variant: { id: lineItem.Variant ? String(lineItem.Variant.ID) : '', sku: lineItem.ID, diff --git a/packages/ordercloud/src/api/utils/fetch-rest.ts b/packages/ordercloud/src/api/utils/fetch-rest.ts index 5b70b9dff..3724ebe84 100644 --- a/packages/ordercloud/src/api/utils/fetch-rest.ts +++ b/packages/ordercloud/src/api/utils/fetch-rest.ts @@ -1,11 +1,7 @@ -import vercelFetch from '@vercel/fetch' import { FetcherError } from '@vercel/commerce/utils/errors' -import { CustomNodeJsGlobal } from '../../types/node'; - import { OrdercloudConfig } from '../index' -// Get an instance to vercel fetch -const fetch = vercelFetch() +export let token: string | null = null // Get token util async function getToken({ @@ -16,7 +12,12 @@ async function getToken({ baseUrl: string clientId: string clientSecret?: string -}): Promise { +}): Promise<{ + access_token: string + expires_in: number + refresh_token: string + token_type: string +}> { // If not, get a new one and store it const authResponse = await fetch(`${baseUrl}/oauth/token`, { method: 'POST', @@ -32,6 +33,8 @@ async function getToken({ // Get the body of it const error = await authResponse.json() + console.log(JSON.stringify(error, null, 2)) + // And return an error throw new FetcherError({ errors: [{ message: error.error_description.Code }], @@ -40,9 +43,7 @@ async function getToken({ } // Return the token - return authResponse - .json() - .then((response: { access_token: string }) => response.access_token) + return authResponse.json() } export async function fetchData(opts: { @@ -74,12 +75,18 @@ export async function fetchData(opts: { // If something failed getting the data response if (!dataResponse.ok) { - // Get the body of it - const error = await dataResponse.textConverted() + let errors - // And return an error + try { + // Get the body of it + const error = await dataResponse.json() + errors = error.Errors + } catch (e) { + const message = await dataResponse.text() + errors = [{ message }] + } throw new FetcherError({ - errors: [{ message: error || dataResponse.statusText }], + errors, status: dataResponse.status, }) } @@ -93,42 +100,6 @@ export async function fetchData(opts: { } } -export const createMiddlewareFetcher: ( - getConfig: () => OrdercloudConfig -) => ( - method: string, - path: string, - body?: Record, - fetchOptions?: Record -) => Promise = - (getConfig) => - async ( - method: string, - path: string, - body?: Record, - fetchOptions?: Record - ) => { - // Get provider config - const config = getConfig() - - // Get a token - const token = await getToken({ - baseUrl: config.commerceUrl, - clientId: process.env.ORDERCLOUD_MIDDLEWARE_CLIENT_ID as string, - clientSecret: process.env.ORDERCLOUD_MIDDLEWARE_CLIENT_SECRET, - }) - - // Return the data and specify the expected type - return fetchData({ - token, - fetchOptions, - method, - config, - path, - body, - }) - } - export const createBuyerFetcher: ( getConfig: () => OrdercloudConfig ) => ( @@ -144,28 +115,27 @@ export const createBuyerFetcher: ( body?: Record, fetchOptions?: Record ) => { - const customGlobal = global as unknown as CustomNodeJsGlobal; + if (fetchOptions?.token) { + token = fetchOptions?.token + } // Get provider config const config = getConfig() + let meta: any = {} - // If a token was passed, set it on global - if (fetchOptions?.token) { - customGlobal.token = fetchOptions.token - } - - // Get a token - if (!customGlobal.token) { - customGlobal.token = await getToken({ + if (!token) { + const newToken = await getToken({ baseUrl: config.commerceUrl, clientId: process.env.ORDERCLOUD_BUYER_CLIENT_ID as string, }) + token = newToken.access_token + meta.token = newToken } // Return the data and specify the expected type const data = await fetchData({ - token: customGlobal.token as string, + token, fetchOptions, config, method, @@ -175,6 +145,6 @@ export const createBuyerFetcher: ( return { ...data, - meta: { token: customGlobal.token as string }, + meta, } } diff --git a/packages/ordercloud/src/cart/use-add-item.tsx b/packages/ordercloud/src/cart/use-add-item.tsx index c9d315301..44852390b 100644 --- a/packages/ordercloud/src/cart/use-add-item.tsx +++ b/packages/ordercloud/src/cart/use-add-item.tsx @@ -10,7 +10,7 @@ export default useAddItem as UseAddItem export const handler: MutationHook = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'POST', }, async fetcher({ input: item, options, fetch }) { diff --git a/packages/ordercloud/src/cart/use-cart.tsx b/packages/ordercloud/src/cart/use-cart.tsx index 01c3a1cab..58f49b142 100644 --- a/packages/ordercloud/src/cart/use-cart.tsx +++ b/packages/ordercloud/src/cart/use-cart.tsx @@ -8,7 +8,7 @@ export default useCart as UseCart export const handler: SWRHook = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'GET', }, useHook: ({ useData }) => diff --git a/packages/ordercloud/src/cart/use-remove-item.tsx b/packages/ordercloud/src/cart/use-remove-item.tsx index fba15f8d5..b9e43cf52 100644 --- a/packages/ordercloud/src/cart/use-remove-item.tsx +++ b/packages/ordercloud/src/cart/use-remove-item.tsx @@ -2,12 +2,18 @@ import type { MutationHookContext, HookFetcherContext, } from '@vercel/commerce/utils/types' -import type { Cart, LineItem, RemoveItemHook } from '@vercel/commerce/types/cart' +import type { + Cart, + LineItem, + RemoveItemHook, +} from '@vercel/commerce/types/cart' import { useCallback } from 'react' import { ValidationError } from '@vercel/commerce/utils/errors' -import useRemoveItem, { UseRemoveItem } from '@vercel/commerce/cart/use-remove-item' +import useRemoveItem, { + UseRemoveItem, +} from '@vercel/commerce/cart/use-remove-item' import useCart from './use-cart' @@ -23,7 +29,7 @@ export default useRemoveItem as UseRemoveItem export const handler = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'DELETE', }, async fetcher({ diff --git a/packages/ordercloud/src/cart/use-update-item.tsx b/packages/ordercloud/src/cart/use-update-item.tsx index 7a5f6fcf7..bfbf2902c 100644 --- a/packages/ordercloud/src/cart/use-update-item.tsx +++ b/packages/ordercloud/src/cart/use-update-item.tsx @@ -9,7 +9,9 @@ import debounce from 'lodash.debounce' import { MutationHook } from '@vercel/commerce/utils/types' import { ValidationError } from '@vercel/commerce/utils/errors' -import useUpdateItem, { UseUpdateItem } from '@vercel/commerce/cart/use-update-item' +import useUpdateItem, { + UseUpdateItem, +} from '@vercel/commerce/cart/use-update-item' import { handler as removeItemHandler } from './use-remove-item' import useCart from './use-cart' @@ -22,7 +24,7 @@ export default useUpdateItem as UseUpdateItem export const handler: MutationHook = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'PUT', }, async fetcher({ diff --git a/packages/ordercloud/src/checkout/use-checkout.tsx b/packages/ordercloud/src/checkout/use-checkout.tsx index 67d92708f..17897a592 100644 --- a/packages/ordercloud/src/checkout/use-checkout.tsx +++ b/packages/ordercloud/src/checkout/use-checkout.tsx @@ -11,7 +11,7 @@ export default useCheckout as UseCheckout export const handler: SWRHook = { fetchOptions: { - url: '/api/checkout', + url: '/api/commerce/checkout', method: 'GET', }, useHook: ({ useData }) => diff --git a/packages/ordercloud/src/checkout/use-submit-checkout.tsx b/packages/ordercloud/src/checkout/use-submit-checkout.tsx index 79c438499..d8d78ef8b 100644 --- a/packages/ordercloud/src/checkout/use-submit-checkout.tsx +++ b/packages/ordercloud/src/checkout/use-submit-checkout.tsx @@ -10,7 +10,7 @@ export default useSubmitCheckout as UseSubmitCheckout export const handler: MutationHook = { fetchOptions: { - url: '/api/checkout', + url: '/api/commerce/checkout', method: 'POST', }, async fetcher({ input: item, options, fetch }) { diff --git a/packages/ordercloud/src/customer/address/use-add-item.tsx b/packages/ordercloud/src/customer/address/use-add-item.tsx index 798f5fe22..42bd53d0f 100644 --- a/packages/ordercloud/src/customer/address/use-add-item.tsx +++ b/packages/ordercloud/src/customer/address/use-add-item.tsx @@ -2,14 +2,16 @@ import type { AddItemHook } from '@vercel/commerce/types/customer/address' import type { MutationHook } from '@vercel/commerce/utils/types' import { useCallback } from 'react' -import useAddItem, { UseAddItem } from '@vercel/commerce/customer/address/use-add-item' +import useAddItem, { + UseAddItem, +} from '@vercel/commerce/customer/address/use-add-item' import useAddresses from './use-addresses' export default useAddItem as UseAddItem export const handler: MutationHook = { fetchOptions: { - url: '/api/customer/address', + url: '/api/commerce/customer/address', method: 'POST', }, async fetcher({ input: item, options, fetch }) { @@ -27,9 +29,7 @@ export const handler: MutationHook = { return useCallback( async function addItem(input) { const data = await fetch({ input }) - - await mutate([data], false) - + await mutate(data ? [data] : [], false) return data }, [fetch, mutate] diff --git a/packages/ordercloud/src/customer/address/use-addresses.tsx b/packages/ordercloud/src/customer/address/use-addresses.tsx index fd6807a25..704b02c2f 100644 --- a/packages/ordercloud/src/customer/address/use-addresses.tsx +++ b/packages/ordercloud/src/customer/address/use-addresses.tsx @@ -10,7 +10,7 @@ export default useAddresses as UseAddresses export const handler: SWRHook = { fetchOptions: { - url: '/api/customer/address', + url: '/api/commerce/customer/address', method: 'GET', }, useHook: ({ useData }) => diff --git a/packages/ordercloud/src/customer/address/use-remove-item.tsx b/packages/ordercloud/src/customer/address/use-remove-item.tsx index ac805dbc5..0e8cf9b17 100644 --- a/packages/ordercloud/src/customer/address/use-remove-item.tsx +++ b/packages/ordercloud/src/customer/address/use-remove-item.tsx @@ -2,7 +2,10 @@ import type { MutationHookContext, HookFetcherContext, } from '@vercel/commerce/utils/types' -import type { Address, RemoveItemHook } from '@vercel/commerce/types/customer/address' +import type { + Address, + RemoveItemHook, +} from '@vercel/commerce/types/customer/address' import { useCallback } from 'react' @@ -14,7 +17,7 @@ import useRemoveItem, { import useAddresses from './use-addresses' export type RemoveItemFn = T extends Address - ? (input?: RemoveItemActionInput) => Promise

+ ? (input?: RemoveItemActionInput) => Promise
: (input: RemoveItemActionInput) => Promise
export type RemoveItemActionInput = T extends Address @@ -25,7 +28,7 @@ export default useRemoveItem as UseRemoveItem export const handler = { fetchOptions: { - url: '/api/customer/address', + url: '/api/commerce/customer/address', method: 'DELETE', }, async fetcher({ diff --git a/packages/ordercloud/src/customer/address/use-update-item.tsx b/packages/ordercloud/src/customer/address/use-update-item.tsx index d65956b7b..a657c4293 100644 --- a/packages/ordercloud/src/customer/address/use-update-item.tsx +++ b/packages/ordercloud/src/customer/address/use-update-item.tsx @@ -2,7 +2,10 @@ import type { HookFetcherContext, MutationHookContext, } from '@vercel/commerce/utils/types' -import type { UpdateItemHook, Address } from '@vercel/commerce/types/customer/address' +import type { + UpdateItemHook, + Address, +} from '@vercel/commerce/types/customer/address' import { useCallback } from 'react' @@ -21,7 +24,7 @@ export default useUpdateItem as UseUpdateItem export const handler: MutationHook = { fetchOptions: { - url: '/api/customer/address', + url: '/api/commerce/customer/address', method: 'PUT', }, async fetcher({ diff --git a/packages/ordercloud/src/customer/card/use-add-item.tsx b/packages/ordercloud/src/customer/card/use-add-item.tsx index 8e7d27885..2cfdc5312 100644 --- a/packages/ordercloud/src/customer/card/use-add-item.tsx +++ b/packages/ordercloud/src/customer/card/use-add-item.tsx @@ -2,14 +2,16 @@ import type { AddItemHook } from '@vercel/commerce/types/customer/card' import type { MutationHook } from '@vercel/commerce/utils/types' import { useCallback } from 'react' -import useAddItem, { UseAddItem } from '@vercel/commerce/customer/card/use-add-item' +import useAddItem, { + UseAddItem, +} from '@vercel/commerce/customer/card/use-add-item' import useCards from './use-cards' export default useAddItem as UseAddItem export const handler: MutationHook = { fetchOptions: { - url: '/api/customer/card', + url: '/api/commerce/customer/card', method: 'POST', }, async fetcher({ input: item, options, fetch }) { @@ -28,7 +30,7 @@ export const handler: MutationHook = { async function addItem(input) { const data = await fetch({ input }) - await mutate([data], false) + await mutate(data ? [data] : [], false) return data }, diff --git a/packages/ordercloud/src/customer/card/use-cards.tsx b/packages/ordercloud/src/customer/card/use-cards.tsx index 6438543e1..ef5fc5282 100644 --- a/packages/ordercloud/src/customer/card/use-cards.tsx +++ b/packages/ordercloud/src/customer/card/use-cards.tsx @@ -8,7 +8,7 @@ export default useCard as UseCards export const handler: SWRHook = { fetchOptions: { - url: '/api/customer/card', + url: '/api/commerce/customer/card', method: 'GET', }, useHook: ({ useData }) => diff --git a/packages/ordercloud/src/customer/card/use-remove-item.tsx b/packages/ordercloud/src/customer/card/use-remove-item.tsx index 23979bf3f..f815a9b51 100644 --- a/packages/ordercloud/src/customer/card/use-remove-item.tsx +++ b/packages/ordercloud/src/customer/card/use-remove-item.tsx @@ -14,7 +14,7 @@ import useRemoveItem, { import useCards from './use-cards' export type RemoveItemFn = T extends Card - ? (input?: RemoveItemActionInput) => Promise + ? (input?: RemoveItemActionInput) => Promise : (input: RemoveItemActionInput) => Promise export type RemoveItemActionInput = T extends Card @@ -25,7 +25,7 @@ export default useRemoveItem as UseRemoveItem export const handler = { fetchOptions: { - url: '/api/customer/card', + url: '/api/commerce/customer/card', method: 'DELETE', }, async fetcher({ diff --git a/packages/ordercloud/src/customer/card/use-update-item.tsx b/packages/ordercloud/src/customer/card/use-update-item.tsx index faa900744..73d792af0 100644 --- a/packages/ordercloud/src/customer/card/use-update-item.tsx +++ b/packages/ordercloud/src/customer/card/use-update-item.tsx @@ -21,7 +21,7 @@ export default useUpdateItem as UseUpdateItem export const handler: MutationHook = { fetchOptions: { - url: '/api/customer/card', + url: '/api/commerce/customer/card', method: 'PUT', }, async fetcher({ diff --git a/packages/ordercloud/src/product/use-search.tsx b/packages/ordercloud/src/product/use-search.tsx index 0cb54e92c..83d5373c1 100644 --- a/packages/ordercloud/src/product/use-search.tsx +++ b/packages/ordercloud/src/product/use-search.tsx @@ -5,7 +5,7 @@ export default useSearch as UseSearch export const handler: SWRHook = { fetchOptions: { - url: '/api/catalog/products', + url: '/api/commerce/catalog/products', method: 'GET', }, fetcher({ input: { search, categoryId, brandId, sort }, options, fetch }) { diff --git a/packages/ordercloud/src/types/product.ts b/packages/ordercloud/src/types/product.ts index 8ccb778d2..e0338ee2d 100644 --- a/packages/ordercloud/src/types/product.ts +++ b/packages/ordercloud/src/types/product.ts @@ -1,4 +1,4 @@ -interface RawVariantSpec { +export interface RawVariantSpec { SpecID: string Name: string OptionID: string diff --git a/packages/saleor/package.json b/packages/saleor/package.json index a94d31ad8..8fc0b3d91 100644 --- a/packages/saleor/package.json +++ b/packages/saleor/package.json @@ -50,10 +50,8 @@ }, "dependencies": { "@vercel/commerce": "workspace:*", - "@vercel/fetch": "^6.2.0", "js-cookie": "^3.0.1", - "lodash.debounce": "^4.0.8", - "node-fetch": "^2.6.7" + "lodash.debounce": "^4.0.8" }, "peerDependencies": { "next": "^12", diff --git a/packages/saleor/src/api/endpoints/cart.ts b/packages/saleor/src/api/endpoints/cart.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/saleor/src/api/endpoints/cart.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/saleor/src/api/endpoints/catalog/products.ts b/packages/saleor/src/api/endpoints/catalog/products.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/saleor/src/api/endpoints/catalog/products.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/saleor/src/api/endpoints/checkout/index.ts b/packages/saleor/src/api/endpoints/checkout/index.ts index 2468c8d97..ccfa16135 100644 --- a/packages/saleor/src/api/endpoints/checkout/index.ts +++ b/packages/saleor/src/api/endpoints/checkout/index.ts @@ -6,9 +6,11 @@ export type CheckoutAPI = GetAPISchema export type CheckoutEndpoint = CheckoutAPI['endpoint'] -const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ req, res, config }) => { - try { - const html = ` +const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ + req, + config, +}) => { + const html = ` @@ -30,16 +32,11 @@ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ req, r ` - res.status(200) - res.setHeader('Content-Type', 'text/html') - res.write(html) - res.end() - } catch (error) { - console.error(error) - - const message = 'An unexpected error ocurred' - - res.status(500).json({ data: null, errors: [{ message }] }) + return { + html, + headers: { + 'Content-Type': 'text/html', + }, } } diff --git a/packages/saleor/src/api/endpoints/customer/address.ts b/packages/saleor/src/api/endpoints/customer/address.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/saleor/src/api/endpoints/customer/address.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/saleor/src/api/endpoints/customer/card.ts b/packages/saleor/src/api/endpoints/customer/card.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/saleor/src/api/endpoints/customer/card.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/saleor/src/api/endpoints/customer/index.ts b/packages/saleor/src/api/endpoints/customer/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/saleor/src/api/endpoints/customer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/saleor/src/api/endpoints/index.ts b/packages/saleor/src/api/endpoints/index.ts new file mode 100644 index 000000000..4c06a91c9 --- /dev/null +++ b/packages/saleor/src/api/endpoints/index.ts @@ -0,0 +1,12 @@ +import type { Provider, SaleorAPI } from '..' + +import createEndpoints from '@vercel/commerce/api/endpoints' +import checkout from './checkout' + +const endpoints = { + checkout, +} + +export default function saleorAPI(commerce: SaleorAPI) { + return createEndpoints(commerce, endpoints) +} diff --git a/packages/saleor/src/api/endpoints/login.ts b/packages/saleor/src/api/endpoints/login.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/saleor/src/api/endpoints/login.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/saleor/src/api/endpoints/logout.ts b/packages/saleor/src/api/endpoints/logout.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/saleor/src/api/endpoints/logout.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/saleor/src/api/endpoints/signup.ts b/packages/saleor/src/api/endpoints/signup.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/saleor/src/api/endpoints/signup.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/saleor/src/api/endpoints/wishlist.ts b/packages/saleor/src/api/endpoints/wishlist.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/saleor/src/api/endpoints/wishlist.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/saleor/src/api/operations/get-all-pages.ts b/packages/saleor/src/api/operations/get-all-pages.ts index e53cc8f31..0da523a88 100644 --- a/packages/saleor/src/api/operations/get-all-pages.ts +++ b/packages/saleor/src/api/operations/get-all-pages.ts @@ -30,9 +30,7 @@ export default function getAllPagesOperation({ { variables }, { ...(locale && { - headers: { - 'Accept-Language': locale, - }, + 'Accept-Language': locale, }), } ) diff --git a/packages/saleor/src/api/operations/get-all-products.ts b/packages/saleor/src/api/operations/get-all-products.ts index ae002d165..9a92e2f7a 100644 --- a/packages/saleor/src/api/operations/get-all-products.ts +++ b/packages/saleor/src/api/operations/get-all-products.ts @@ -39,9 +39,7 @@ export default function getAllProductsOperation({ { variables }, { ...(locale && { - headers: { - 'Accept-Language': locale, - }, + 'Accept-Language': locale, }), } ) diff --git a/packages/saleor/src/api/operations/get-page.ts b/packages/saleor/src/api/operations/get-page.ts index 6fa427691..9d260713d 100644 --- a/packages/saleor/src/api/operations/get-page.ts +++ b/packages/saleor/src/api/operations/get-page.ts @@ -28,9 +28,7 @@ export default function getPageOperation({ { variables }, { ...(locale && { - headers: { - 'Accept-Language': locale, - }, + 'Accept-Language': locale, }), } ) diff --git a/packages/saleor/src/api/operations/get-product.ts b/packages/saleor/src/api/operations/get-product.ts index cc2bf9a7a..c9bdf364b 100644 --- a/packages/saleor/src/api/operations/get-product.ts +++ b/packages/saleor/src/api/operations/get-product.ts @@ -32,9 +32,7 @@ export default function getProductOperation({ { variables }, { ...(locale && { - headers: { - 'Accept-Language': locale, - }, + 'Accept-Language': locale, }), } ) diff --git a/packages/saleor/src/api/operations/login.ts b/packages/saleor/src/api/operations/login.ts index 76d1e7844..eac85e90b 100644 --- a/packages/saleor/src/api/operations/login.ts +++ b/packages/saleor/src/api/operations/login.ts @@ -1,11 +1,12 @@ -import type { ServerResponse } from 'http' import type { OperationContext } from '@vercel/commerce/api/operations' import type { Provider, SaleorConfig } from '..' import { throwUserErrors } from '../../utils' import * as Mutation from '../../utils/mutations' -export default function loginOperation({ commerce }: OperationContext) { +export default function loginOperation({ + commerce, +}: OperationContext) { async function login({ query = Mutation.SessionCreate, variables, @@ -13,7 +14,7 @@ export default function loginOperation({ commerce }: OperationContext) }: { query?: string variables: any - res: ServerResponse + res: Response config?: SaleorConfig }): Promise { config = commerce.getConfig(config) diff --git a/packages/saleor/src/api/utils/fetch-graphql-api.ts b/packages/saleor/src/api/utils/fetch-graphql-api.ts index de4d45aae..313b57a05 100644 --- a/packages/saleor/src/api/utils/fetch-graphql-api.ts +++ b/packages/saleor/src/api/utils/fetch-graphql-api.ts @@ -1,23 +1,25 @@ import type { GraphQLFetcher } from '@vercel/commerce/api' -import fetch from './fetch' import { API_URL } from '../../const' import { getError } from '../../utils/handle-fetch-response' import { getCommerceApi } from '..' import { getToken } from '../../utils/index' -const fetchGraphqlApi: GraphQLFetcher = async (query: string, { variables } = {}, fetchOptions) => { +const fetchGraphqlApi: GraphQLFetcher = async ( + query: string, + { variables } = {}, + headers?: HeadersInit +) => { const config = getCommerceApi().getConfig() const token = getToken() const res = await fetch(API_URL!, { - ...fetchOptions, method: 'POST', headers: { ...(token && { Authorization: `Bearer ${token}`, }), - ...fetchOptions?.headers, + ...headers, 'Content-Type': 'application/json', }, body: JSON.stringify({ diff --git a/packages/saleor/src/api/utils/is-allowed-method.ts b/packages/saleor/src/api/utils/is-allowed-method.ts deleted file mode 100644 index cbaab2251..000000000 --- a/packages/saleor/src/api/utils/is-allowed-method.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next' - -export default function isAllowedMethod(req: NextApiRequest, res: NextApiResponse, allowedMethods: string[]) { - const methods = allowedMethods.includes('OPTIONS') ? allowedMethods : [...allowedMethods, 'OPTIONS'] - - if (!req.method || !methods.includes(req.method)) { - res.status(405) - res.setHeader('Allow', methods.join(', ')) - res.end() - return false - } - - if (req.method === 'OPTIONS') { - res.status(200) - res.setHeader('Allow', methods.join(', ')) - res.setHeader('Content-Length', '0') - res.end() - return false - } - - return true -} diff --git a/packages/saleor/src/fetcher.ts b/packages/saleor/src/fetcher.ts index c1be921c5..4e7c5f194 100644 --- a/packages/saleor/src/fetcher.ts +++ b/packages/saleor/src/fetcher.ts @@ -2,7 +2,12 @@ import { Fetcher } from '@vercel/commerce/utils/types' import { API_URL } from './const' import { getToken, handleFetchResponse } from './utils' -const fetcher: Fetcher = async ({ url = API_URL, method = 'POST', variables, query }) => { +const fetcher: Fetcher = async ({ + url = API_URL, + method = 'POST', + variables, + query, +}) => { const token = getToken() return handleFetchResponse( diff --git a/packages/saleor/src/product/use-search.tsx b/packages/saleor/src/product/use-search.tsx index 9ebb73a2a..fa5a2e165 100644 --- a/packages/saleor/src/product/use-search.tsx +++ b/packages/saleor/src/product/use-search.tsx @@ -12,8 +12,8 @@ export default useSearch as UseSearch export type SearchProductsInput = { search?: string - categoryId?: string | number - brandId?: string | number + categoryId?: string + brandId?: string sort?: string } diff --git a/packages/saleor/src/utils/get-search-variables.ts b/packages/saleor/src/utils/get-search-variables.ts index 5adf938e8..9b1909879 100644 --- a/packages/saleor/src/utils/get-search-variables.ts +++ b/packages/saleor/src/utils/get-search-variables.ts @@ -1,7 +1,12 @@ import { getSortVariables } from './get-sort-variables' import type { SearchProductsInput } from '../product/use-search' -export const getSearchVariables = ({ brandId, search, categoryId, sort }: SearchProductsInput) => { +export const getSearchVariables = ({ + brandId, + search, + categoryId, + sort, +}: SearchProductsInput) => { const sortBy = { field: 'NAME', direction: 'ASC', diff --git a/packages/sfcc/package.json b/packages/sfcc/package.json index 7a8810455..375a72748 100644 --- a/packages/sfcc/package.json +++ b/packages/sfcc/package.json @@ -48,9 +48,7 @@ }, "dependencies": { "@vercel/commerce": "workspace:*", - "@vercel/fetch": "^6.2.0", - "commerce-sdk": "^2.7.0", - "node-fetch": "^2.6.7" + "commerce-sdk": "^2.7.0" }, "peerDependencies": { "next": "^12", diff --git a/packages/sfcc/src/api/endpoints/cart/index.ts b/packages/sfcc/src/api/endpoints/cart/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/sfcc/src/api/endpoints/cart/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/sfcc/src/api/endpoints/catalog/products/get-products.ts b/packages/sfcc/src/api/endpoints/catalog/products/get-products.ts index 3b676e5ef..6a7ea9b0b 100644 --- a/packages/sfcc/src/api/endpoints/catalog/products/get-products.ts +++ b/packages/sfcc/src/api/endpoints/catalog/products/get-products.ts @@ -2,9 +2,7 @@ import { normalizeSearchProducts } from '../../../utils/normalise-product' import { ProductsEndpoint } from '.' const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({ - req, - res, - body: { search, categoryId, brandId, sort }, + body: { search, categoryId }, config, }) => { const { sdk } = config @@ -20,13 +18,15 @@ const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({ limit: 20, }, }) + let products = [] let found = false if (searchResults.total) { found = true products = normalizeSearchProducts(searchResults.hits) as any[] } - res.status(200).json({ data: { products, found } }) + + return { data: { products, found } } } export default getProducts diff --git a/packages/sfcc/src/api/endpoints/checkout/index.ts b/packages/sfcc/src/api/endpoints/checkout/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/sfcc/src/api/endpoints/checkout/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/sfcc/src/api/endpoints/customer/address.ts b/packages/sfcc/src/api/endpoints/customer/address.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/sfcc/src/api/endpoints/customer/address.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/sfcc/src/api/endpoints/customer/card.ts b/packages/sfcc/src/api/endpoints/customer/card.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/sfcc/src/api/endpoints/customer/card.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/sfcc/src/api/endpoints/customer/index.ts b/packages/sfcc/src/api/endpoints/customer/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/sfcc/src/api/endpoints/customer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/sfcc/src/api/endpoints/index.ts b/packages/sfcc/src/api/endpoints/index.ts new file mode 100644 index 000000000..e848954c9 --- /dev/null +++ b/packages/sfcc/src/api/endpoints/index.ts @@ -0,0 +1,13 @@ +import type { Provider, SFCCProviderAPI } from '..' + +import createEndpoints from '@vercel/commerce/api/endpoints' + +import products from './catalog/products' + +const endpoints = { + 'catalog/products': products, +} + +export default function sfccApi(commerce: SFCCProviderAPI) { + return createEndpoints(commerce, endpoints) +} diff --git a/packages/sfcc/src/api/endpoints/login/index.ts b/packages/sfcc/src/api/endpoints/login/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/sfcc/src/api/endpoints/login/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/sfcc/src/api/endpoints/logout/index.ts b/packages/sfcc/src/api/endpoints/logout/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/sfcc/src/api/endpoints/logout/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/sfcc/src/api/endpoints/signup/index.ts b/packages/sfcc/src/api/endpoints/signup/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/sfcc/src/api/endpoints/signup/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/sfcc/src/api/endpoints/wishlist/index.tsx b/packages/sfcc/src/api/endpoints/wishlist/index.tsx deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/sfcc/src/api/endpoints/wishlist/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/sfcc/src/api/utils/fetch-local.ts b/packages/sfcc/src/api/utils/fetch-local.ts index 5a2bab076..58e801517 100644 --- a/packages/sfcc/src/api/utils/fetch-local.ts +++ b/packages/sfcc/src/api/utils/fetch-local.ts @@ -1,17 +1,15 @@ import { FetcherError } from '@vercel/commerce/utils/errors' import type { GraphQLFetcher } from '@vercel/commerce/api' import type { SFCCConfig } from '../index' -import fetch from './fetch' const fetchGraphqlApi: (getConfig: () => SFCCConfig) => GraphQLFetcher = (getConfig) => - async (query: string, { variables, preview } = {}, fetchOptions) => { + async (query: string, { variables, preview } = {}, headers?: HeadersInit) => { const config = getConfig() const res = await fetch(config.commerceUrl, { - ...fetchOptions, method: 'POST', headers: { - ...fetchOptions?.headers, + ...headers, 'Content-Type': 'application/json', }, body: JSON.stringify({ diff --git a/packages/sfcc/src/product/use-search.tsx b/packages/sfcc/src/product/use-search.tsx index bb5e97160..83d5373c1 100644 --- a/packages/sfcc/src/product/use-search.tsx +++ b/packages/sfcc/src/product/use-search.tsx @@ -5,11 +5,10 @@ export default useSearch as UseSearch export const handler: SWRHook = { fetchOptions: { - url: '/api/catalog/products', + url: '/api/commerce/catalog/products', method: 'GET', }, fetcher({ input: { search, categoryId, brandId, sort }, options, fetch }) { - console.log('search', search, categoryId, options) // Use a dummy base as we only care about the relative path const url = new URL(options.url!, 'http://a') diff --git a/packages/shopify/package.json b/packages/shopify/package.json index b903df537..cdf0e82ff 100644 --- a/packages/shopify/package.json +++ b/packages/shopify/package.json @@ -50,8 +50,6 @@ }, "dependencies": { "@vercel/commerce": "workspace:*", - "@vercel/fetch": "^6.2.0", - "node-fetch": "^2.6.7", "js-cookie": "^3.0.1", "lodash.debounce": "^4.0.8" }, diff --git a/packages/shopify/src/api/endpoints/cart.ts b/packages/shopify/src/api/endpoints/cart.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/shopify/src/api/endpoints/cart.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/shopify/src/api/endpoints/catalog/products/get-products.ts b/packages/shopify/src/api/endpoints/catalog/products/get-products.ts index 1e8829e78..4f31300ce 100644 --- a/packages/shopify/src/api/endpoints/catalog/products/get-products.ts +++ b/packages/shopify/src/api/endpoints/catalog/products/get-products.ts @@ -1,26 +1,14 @@ 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: ProductsEndpoint['handlers']['getProducts'] = async ({ - res, - body: { search, categoryId, brandId, sort }, - config, commerce, }) => { - res.status(200).json({ - data: { - products: [], - found: false, - }, - }) + const { products } = await commerce.getAllProducts() + + const found = !!products.length + + return { data: { products, found } } } export default getProducts diff --git a/packages/shopify/src/api/endpoints/catalog/products/index.ts b/packages/shopify/src/api/endpoints/catalog/products/index.ts index df680453a..0ae4dd66f 100644 --- a/packages/shopify/src/api/endpoints/catalog/products/index.ts +++ b/packages/shopify/src/api/endpoints/catalog/products/index.ts @@ -1,8 +1,9 @@ -import { GetAPISchema, createEndpoint } from '@vercel/commerce/api' -import productsEndpoint from '@vercel/commerce/api/endpoints/catalog/products' +import { type GetAPISchema, createEndpoint } from '@vercel/commerce/api' import type { ProductsSchema } from '@vercel/commerce/types/product' import type { ShopifyAPI } from '../../..' +import productsEndpoint from '@vercel/commerce/api/endpoints/catalog/products' + import getProducts from './get-products' export type ProductsAPI = GetAPISchema diff --git a/packages/shopify/src/api/endpoints/checkout/get-checkout.ts b/packages/shopify/src/api/endpoints/checkout/get-checkout.ts index 188915022..d9be6f607 100644 --- a/packages/shopify/src/api/endpoints/checkout/get-checkout.ts +++ b/packages/shopify/src/api/endpoints/checkout/get-checkout.ts @@ -8,19 +8,18 @@ import type { CheckoutEndpoint } from '.' const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ req, - res, config, }) => { const { cookies } = req - const checkoutUrl = cookies[SHOPIFY_CHECKOUT_URL_COOKIE] - const customerCookie = cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE] + const checkoutUrl = cookies.get(SHOPIFY_CHECKOUT_URL_COOKIE) + const customerCookie = cookies.get(SHOPIFY_CUSTOMER_TOKEN_COOKIE) if (customerCookie) { try { await config.fetch(associateCustomerWithCheckoutMutation, { variables: { - checkoutId: cookies[SHOPIFY_CHECKOUT_ID_COOKIE], - customerAccessToken: cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE], + checkoutId: cookies.get(SHOPIFY_CHECKOUT_ID_COOKIE), + customerAccessToken: cookies.get(SHOPIFY_CUSTOMER_TOKEN_COOKIE), }, }) } catch (error) { @@ -28,11 +27,7 @@ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ } } - if (checkoutUrl) { - res.redirect(checkoutUrl) - } else { - res.redirect('/cart') - } + return { redirectTo: checkoutUrl ?? '/cart' } } export default getCheckout diff --git a/packages/shopify/src/api/endpoints/customer/address.ts b/packages/shopify/src/api/endpoints/customer/address.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/shopify/src/api/endpoints/customer/address.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/shopify/src/api/endpoints/customer/card.ts b/packages/shopify/src/api/endpoints/customer/card.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/shopify/src/api/endpoints/customer/card.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/shopify/src/api/endpoints/customer/index.ts b/packages/shopify/src/api/endpoints/customer/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/shopify/src/api/endpoints/customer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/shopify/src/api/endpoints/index.ts b/packages/shopify/src/api/endpoints/index.ts new file mode 100644 index 000000000..4f539eac7 --- /dev/null +++ b/packages/shopify/src/api/endpoints/index.ts @@ -0,0 +1,14 @@ +import type { Provider, ShopifyAPI } from '..' + +import createEndpoints from '@vercel/commerce/api/endpoints' +import checkout from './checkout' +import products from './catalog/products' + +const endpoints = { + checkout, + 'catalog/products': products, +} + +export default function shopifyAPI(commerce: ShopifyAPI) { + return createEndpoints(commerce, endpoints) +} diff --git a/packages/shopify/src/api/endpoints/login.ts b/packages/shopify/src/api/endpoints/login.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/shopify/src/api/endpoints/login.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/shopify/src/api/endpoints/logout.ts b/packages/shopify/src/api/endpoints/logout.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/shopify/src/api/endpoints/logout.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/shopify/src/api/endpoints/signup.ts b/packages/shopify/src/api/endpoints/signup.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/shopify/src/api/endpoints/signup.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/shopify/src/api/endpoints/wishlist.ts b/packages/shopify/src/api/endpoints/wishlist.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/shopify/src/api/endpoints/wishlist.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/shopify/src/api/operations/get-all-pages.ts b/packages/shopify/src/api/operations/get-all-pages.ts index 55c1d7d69..f8cde77f8 100644 --- a/packages/shopify/src/api/operations/get-all-pages.ts +++ b/packages/shopify/src/api/operations/get-all-pages.ts @@ -51,9 +51,7 @@ export default function getAllPagesOperation({ }, { ...(locale && { - headers: { - 'Accept-Language': locale, - }, + 'Accept-Language': locale, }), } ) diff --git a/packages/shopify/src/api/operations/get-all-products.ts b/packages/shopify/src/api/operations/get-all-products.ts index fcab20128..6ae4885ed 100644 --- a/packages/shopify/src/api/operations/get-all-products.ts +++ b/packages/shopify/src/api/operations/get-all-products.ts @@ -49,9 +49,7 @@ export default function getAllProductsOperation({ { variables }, { ...(locale && { - headers: { - 'Accept-Language': locale, - }, + 'Accept-Language': locale, }), } ) diff --git a/packages/shopify/src/api/operations/get-page.ts b/packages/shopify/src/api/operations/get-page.ts index e6ba19ce5..a98cae79b 100644 --- a/packages/shopify/src/api/operations/get-page.ts +++ b/packages/shopify/src/api/operations/get-page.ts @@ -50,9 +50,7 @@ export default function getPageOperation({ }, { ...(locale && { - headers: { - 'Accept-Language': locale, - }, + 'Accept-Language': locale, }), } ) diff --git a/packages/shopify/src/api/operations/get-product.ts b/packages/shopify/src/api/operations/get-product.ts index 76a3c865f..e8aa28120 100644 --- a/packages/shopify/src/api/operations/get-product.ts +++ b/packages/shopify/src/api/operations/get-product.ts @@ -48,9 +48,7 @@ export default function getProductOperation({ }, { ...(locale && { - headers: { - 'Accept-Language': locale, - }, + 'Accept-Language': locale, }), } ) diff --git a/packages/shopify/src/api/operations/login.ts b/packages/shopify/src/api/operations/login.ts index 89f402256..3f4419a4a 100644 --- a/packages/shopify/src/api/operations/login.ts +++ b/packages/shopify/src/api/operations/login.ts @@ -1,4 +1,3 @@ -import type { ServerResponse } from 'http' import type { OperationContext } from '@vercel/commerce/api/operations' import type { LoginOperation } from '@vercel/commerce/types/login' import type { ShopifyConfig, Provider } from '..' @@ -19,7 +18,7 @@ export default function loginOperation({ }: { query?: string variables: T['variables'] - res: ServerResponse + res: Response config?: ShopifyConfig }): Promise { config = commerce.getConfig(config) diff --git a/packages/shopify/src/api/utils/fetch-graphql-api.ts b/packages/shopify/src/api/utils/fetch-graphql-api.ts index 9f28d5218..1eac16ef1 100644 --- a/packages/shopify/src/api/utils/fetch-graphql-api.ts +++ b/packages/shopify/src/api/utils/fetch-graphql-api.ts @@ -1,5 +1,4 @@ import type { GraphQLFetcher } from '@vercel/commerce/api' -import fetch from './fetch' import { API_URL, API_TOKEN } from '../../const' import { getError } from '../../utils/handle-fetch-response' @@ -7,15 +6,14 @@ import { getError } from '../../utils/handle-fetch-response' const fetchGraphqlApi: GraphQLFetcher = async ( query: string, { variables } = {}, - fetchOptions + headers?: HeadersInit ) => { try { const res = await fetch(API_URL, { - ...fetchOptions, method: 'POST', headers: { 'X-Shopify-Storefront-Access-Token': API_TOKEN!, - ...fetchOptions?.headers, + ...headers, 'Content-Type': 'application/json', }, body: JSON.stringify({ diff --git a/packages/shopify/src/utils/get-categories.ts b/packages/shopify/src/utils/get-categories.ts index 2bae3c3b5..e21592ee1 100644 --- a/packages/shopify/src/utils/get-categories.ts +++ b/packages/shopify/src/utils/get-categories.ts @@ -17,9 +17,7 @@ const getCategories = async ({ }, { ...(locale && { - headers: { - 'Accept-Language': locale, - }, + 'Accept-Language': locale, }), } ) diff --git a/packages/spree/package.json b/packages/spree/package.json index 4ddd9a2ed..ee18a911e 100644 --- a/packages/spree/package.json +++ b/packages/spree/package.json @@ -49,10 +49,8 @@ "dependencies": { "@spree/storefront-api-v2-sdk": "^5.1.1", "@vercel/commerce": "workspace:*", - "@vercel/fetch": "^6.2.0", "js-cookie": "^3.0.1", "lodash.debounce": "^4.0.8", - "node-fetch": "^2.6.7", "swr": "^1.3.0" }, "peerDependencies": { diff --git a/packages/spree/src/api/endpoints/cart/index.ts b/packages/spree/src/api/endpoints/cart/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/spree/src/api/endpoints/cart/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/spree/src/api/endpoints/catalog/index.ts b/packages/spree/src/api/endpoints/catalog/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/spree/src/api/endpoints/catalog/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/spree/src/api/endpoints/catalog/products.ts b/packages/spree/src/api/endpoints/catalog/products.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/spree/src/api/endpoints/catalog/products.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/spree/src/api/endpoints/checkout/get-checkout.ts b/packages/spree/src/api/endpoints/checkout/get-checkout.ts index 985239678..a99c774ab 100644 --- a/packages/spree/src/api/endpoints/checkout/get-checkout.ts +++ b/packages/spree/src/api/endpoints/checkout/get-checkout.ts @@ -2,11 +2,9 @@ import type { CheckoutEndpoint } from '.' const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ req: _request, - res: response, config: _config, }) => { - try { - const html = ` + const html = ` @@ -28,17 +26,11 @@ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ ` - response.status(200) - response.setHeader('Content-Type', 'text/html') - response.write(html) - response.end() - } catch (error) { - console.error(error) - - const message = 'An unexpected error ocurred' - - response.status(500).json({ data: null, errors: [{ message }] }) - } + return new Response(html, { + headers: { + 'content-type': 'text/html', + }, + }) } export default getCheckout diff --git a/packages/spree/src/api/endpoints/customer/address.ts b/packages/spree/src/api/endpoints/customer/address.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/spree/src/api/endpoints/customer/address.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/spree/src/api/endpoints/customer/card.ts b/packages/spree/src/api/endpoints/customer/card.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/spree/src/api/endpoints/customer/card.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/spree/src/api/endpoints/customer/index.ts b/packages/spree/src/api/endpoints/customer/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/spree/src/api/endpoints/customer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/spree/src/api/endpoints/index.ts b/packages/spree/src/api/endpoints/index.ts new file mode 100644 index 000000000..6595f7dcc --- /dev/null +++ b/packages/spree/src/api/endpoints/index.ts @@ -0,0 +1,11 @@ +import type { SpreeApiProvider, SpreeApi } from '..' +import createEndpoints from '@vercel/commerce/api/endpoints' +import checkout from './checkout' + +const endpoints = { + checkout, +} + +export default function spreeAPI(commerce: SpreeApi) { + return createEndpoints(commerce, endpoints) +} diff --git a/packages/spree/src/api/endpoints/login/index.ts b/packages/spree/src/api/endpoints/login/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/spree/src/api/endpoints/login/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/spree/src/api/endpoints/logout/index.ts b/packages/spree/src/api/endpoints/logout/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/spree/src/api/endpoints/logout/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/spree/src/api/endpoints/signup/index.ts b/packages/spree/src/api/endpoints/signup/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/spree/src/api/endpoints/signup/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/spree/src/api/endpoints/wishlist/index.tsx b/packages/spree/src/api/endpoints/wishlist/index.tsx deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/spree/src/api/endpoints/wishlist/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/spree/src/api/utils/create-api-fetch.ts b/packages/spree/src/api/utils/create-api-fetch.ts index b26a2fb11..5e29cf0b7 100644 --- a/packages/spree/src/api/utils/create-api-fetch.ts +++ b/packages/spree/src/api/utils/create-api-fetch.ts @@ -9,7 +9,6 @@ import { GraphQLFetcher, GraphQLFetcherResult } from '@vercel/commerce/api' import createCustomizedFetchFetcher, { fetchResponseKey, } from '../../utils/create-customized-fetch-fetcher' -import fetch, { Request } from 'node-fetch' import type { SpreeSdkResponseWithRawResponse } from '../../types' import prettyPrintSpreeSdkErrors from '../../utils/pretty-print-spree-sdk-errors' diff --git a/packages/spree/src/types/index.ts b/packages/spree/src/types/index.ts index 9142be942..d9bd9bd77 100644 --- a/packages/spree/src/types/index.ts +++ b/packages/spree/src/types/index.ts @@ -5,8 +5,7 @@ import type { JsonApiSingleResponse, } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' import type { ResultResponse } from '@spree/storefront-api-v2-sdk/types/interfaces/ResultResponse' -import type { Response } from '@vercel/fetch' -import type { ProductOption, Product } from '@vercel/commerce/types/product' +import type { ProductOption } from '@vercel/commerce/types/product' import type { Wishlist as CoreWishlist, WishlistItemBody as CoreWishlistItemBody, diff --git a/packages/swell/package.json b/packages/swell/package.json index f5b5908f1..3eb5afdfe 100644 --- a/packages/swell/package.json +++ b/packages/swell/package.json @@ -49,10 +49,8 @@ }, "dependencies": { "@vercel/commerce": "workspace:*", - "@vercel/fetch": "^6.2.0", "js-cookie": "^3.0.1", "lodash.debounce": "^4.0.8", - "node-fetch": "^2.6.7", "swell-js": "^4.0.0-next.0" }, "peerDependencies": { diff --git a/packages/swell/src/api/cart/index.ts b/packages/swell/src/api/cart/index.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/packages/swell/src/api/cart/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/packages/swell/src/api/catalog/index.ts b/packages/swell/src/api/catalog/index.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/packages/swell/src/api/catalog/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/packages/swell/src/api/catalog/products.ts b/packages/swell/src/api/catalog/products.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/packages/swell/src/api/catalog/products.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/packages/swell/src/api/customers/index.ts b/packages/swell/src/api/customers/index.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/packages/swell/src/api/customers/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/packages/swell/src/api/customers/logout.ts b/packages/swell/src/api/customers/logout.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/packages/swell/src/api/customers/logout.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/packages/swell/src/api/customers/signup.ts b/packages/swell/src/api/customers/signup.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/packages/swell/src/api/customers/signup.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/packages/swell/src/api/endpoints/cart.ts b/packages/swell/src/api/endpoints/cart.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/swell/src/api/endpoints/cart.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/swell/src/api/endpoints/catalog/products.ts b/packages/swell/src/api/endpoints/catalog/products.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/swell/src/api/endpoints/catalog/products.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/swell/src/api/endpoints/checkout/index.ts b/packages/swell/src/api/endpoints/checkout/index.ts index 2695ea385..fba531618 100644 --- a/packages/swell/src/api/endpoints/checkout/index.ts +++ b/packages/swell/src/api/endpoints/checkout/index.ts @@ -5,16 +5,14 @@ import checkoutEndpoint from '@vercel/commerce/api/endpoints/checkout' const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ req, - res, - config, }) => { const { cookies } = req - const checkoutUrl = cookies[SWELL_CHECKOUT_URL_COOKIE] + const checkoutUrl = cookies.get(SWELL_CHECKOUT_URL_COOKIE) if (checkoutUrl) { - res.redirect(checkoutUrl) + return { redirectTo: checkoutUrl } } else { - res.redirect('/cart') + return { redirectTo: '/cart' } } } export const handlers: CheckoutEndpoint['handlers'] = { getCheckout } diff --git a/packages/swell/src/api/endpoints/customer/address.ts b/packages/swell/src/api/endpoints/customer/address.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/swell/src/api/endpoints/customer/address.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/swell/src/api/endpoints/customer/card.ts b/packages/swell/src/api/endpoints/customer/card.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/swell/src/api/endpoints/customer/card.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/swell/src/api/endpoints/customer/index.ts b/packages/swell/src/api/endpoints/customer/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/swell/src/api/endpoints/customer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/swell/src/api/endpoints/index.ts b/packages/swell/src/api/endpoints/index.ts new file mode 100644 index 000000000..1fc419bcd --- /dev/null +++ b/packages/swell/src/api/endpoints/index.ts @@ -0,0 +1,12 @@ +import type { Provider, SwellAPI } from '..' + +import createEndpoints from '@vercel/commerce/api/endpoints' +import checkout from './checkout' + +const endpoints = { + checkout, +} + +export default function handler(commerce: SwellAPI) { + return createEndpoints(commerce, endpoints) +} diff --git a/packages/swell/src/api/endpoints/login.ts b/packages/swell/src/api/endpoints/login.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/swell/src/api/endpoints/login.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/swell/src/api/endpoints/logout.ts b/packages/swell/src/api/endpoints/logout.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/swell/src/api/endpoints/logout.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/swell/src/api/endpoints/signup.ts b/packages/swell/src/api/endpoints/signup.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/swell/src/api/endpoints/signup.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/swell/src/api/endpoints/wishlist.ts b/packages/swell/src/api/endpoints/wishlist.ts deleted file mode 100644 index d09c976c3..000000000 --- a/packages/swell/src/api/endpoints/wishlist.ts +++ /dev/null @@ -1 +0,0 @@ -export default function (_commerce: any) {} diff --git a/packages/swell/src/api/index.ts b/packages/swell/src/api/index.ts index 589acae4e..5cdbbaaf3 100644 --- a/packages/swell/src/api/index.ts +++ b/packages/swell/src/api/index.ts @@ -46,6 +46,8 @@ export const provider = { config, operations } export type Provider = typeof provider +export type SwellAPI

= CommerceAPI

+ export function getCommerceApi

( customProvider: P = provider as any ): CommerceAPI

{ diff --git a/packages/swell/src/api/operations/login.ts b/packages/swell/src/api/operations/login.ts index cd1c77655..60508073c 100644 --- a/packages/swell/src/api/operations/login.ts +++ b/packages/swell/src/api/operations/login.ts @@ -1,9 +1,9 @@ -import type { ServerResponse } from 'http' import type { OperationContext, OperationOptions, } from '@vercel/commerce/api/operations' import type { LoginOperation } from '@vercel/commerce/types/login' + import { Provider, SwellConfig } from '..' export default function loginOperation({ @@ -12,14 +12,14 @@ export default function loginOperation({ async function login(opts: { variables: T['variables'] config?: Partial - res: ServerResponse + res: Response }): Promise async function login( opts: { variables: T['variables'] config?: Partial - res: ServerResponse + res: Response } & OperationOptions ): Promise @@ -30,7 +30,7 @@ export default function loginOperation({ }: { query?: string variables: T['variables'] - res: ServerResponse + res: Response config?: Partial }): Promise { const config = commerce.getConfig(cfg) diff --git a/packages/swell/src/api/utils/fetch.ts b/packages/swell/src/api/utils/fetch.ts deleted file mode 100644 index 0b8367102..000000000 --- a/packages/swell/src/api/utils/fetch.ts +++ /dev/null @@ -1,2 +0,0 @@ -import zeitFetch from '@vercel/fetch' -export default zeitFetch() diff --git a/packages/swell/src/api/utils/is-allowed-method.ts b/packages/swell/src/api/utils/is-allowed-method.ts deleted file mode 100644 index 78bbba568..000000000 --- a/packages/swell/src/api/utils/is-allowed-method.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next' - -export default function isAllowedMethod( - req: NextApiRequest, - res: NextApiResponse, - allowedMethods: string[] -) { - const methods = allowedMethods.includes('OPTIONS') - ? allowedMethods - : [...allowedMethods, 'OPTIONS'] - - if (!req.method || !methods.includes(req.method)) { - res.status(405) - res.setHeader('Allow', methods.join(', ')) - res.end() - return false - } - - if (req.method === 'OPTIONS') { - res.status(200) - res.setHeader('Allow', methods.join(', ')) - res.setHeader('Content-Length', '0') - res.end() - return false - } - - return true -} diff --git a/packages/vendure/package.json b/packages/vendure/package.json index 32c9a3117..988d2c0b0 100644 --- a/packages/vendure/package.json +++ b/packages/vendure/package.json @@ -49,9 +49,7 @@ } }, "dependencies": { - "@vercel/commerce": "workspace:*", - "@vercel/fetch": "^6.2.0", - "node-fetch": "^2.6.7" + "@vercel/commerce": "workspace:*" }, "peerDependencies": { "next": "^12", diff --git a/packages/vendure/src/api/endpoints/cart/index.ts b/packages/vendure/src/api/endpoints/cart/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/vendure/src/api/endpoints/cart/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/vendure/src/api/endpoints/catalog/index.ts b/packages/vendure/src/api/endpoints/catalog/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/vendure/src/api/endpoints/catalog/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/vendure/src/api/endpoints/catalog/products.ts b/packages/vendure/src/api/endpoints/catalog/products.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/vendure/src/api/endpoints/catalog/products.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/vendure/src/api/endpoints/checkout/index.ts b/packages/vendure/src/api/endpoints/checkout/index.ts index e063dbf58..71bb8f340 100644 --- a/packages/vendure/src/api/endpoints/checkout/index.ts +++ b/packages/vendure/src/api/endpoints/checkout/index.ts @@ -1,15 +1,9 @@ -import { NextApiHandler } from 'next' import { CommerceAPI, createEndpoint, GetAPISchema } from '@vercel/commerce/api' import { CheckoutSchema } from '@vercel/commerce/types/checkout' import checkoutEndpoint from '@vercel/commerce/api/endpoints/checkout' -const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ - req, - res, - config, -}) => { - try { - const html = ` +const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async () => { + const html = ` @@ -31,16 +25,11 @@ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ ` - res.status(200) - res.setHeader('Content-Type', 'text/html') - res.write(html) - res.end() - } catch (error) { - console.error(error) - - const message = 'An unexpected error ocurred' - - res.status(500).json({ data: null, errors: [{ message }] }) + return { + html, + headers: { + 'Content-Type': 'text/html', + }, } } diff --git a/packages/vendure/src/api/endpoints/customer/address.ts b/packages/vendure/src/api/endpoints/customer/address.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/vendure/src/api/endpoints/customer/address.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/vendure/src/api/endpoints/customer/card.ts b/packages/vendure/src/api/endpoints/customer/card.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/vendure/src/api/endpoints/customer/card.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/vendure/src/api/endpoints/customer/index.ts b/packages/vendure/src/api/endpoints/customer/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/vendure/src/api/endpoints/customer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/vendure/src/api/endpoints/index.ts b/packages/vendure/src/api/endpoints/index.ts new file mode 100644 index 000000000..4cbddc979 --- /dev/null +++ b/packages/vendure/src/api/endpoints/index.ts @@ -0,0 +1,12 @@ +import type { Provider, VendureAPI } from '..' + +import createEndpoints from '@vercel/commerce/api/endpoints' +import checkout from './checkout' + +const endpoints = { + checkout, +} + +export default function vendureAPI(commerce: VendureAPI) { + return createEndpoints(commerce, endpoints) +} \ No newline at end of file diff --git a/packages/vendure/src/api/endpoints/login/index.ts b/packages/vendure/src/api/endpoints/login/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/vendure/src/api/endpoints/login/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/vendure/src/api/endpoints/logout/index.ts b/packages/vendure/src/api/endpoints/logout/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/vendure/src/api/endpoints/logout/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/vendure/src/api/endpoints/signup/index.ts b/packages/vendure/src/api/endpoints/signup/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/vendure/src/api/endpoints/signup/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/vendure/src/api/endpoints/wishlist/index.tsx b/packages/vendure/src/api/endpoints/wishlist/index.tsx deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/vendure/src/api/endpoints/wishlist/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/vendure/src/api/index.ts b/packages/vendure/src/api/index.ts index cbe56be56..41c0189be 100644 --- a/packages/vendure/src/api/index.ts +++ b/packages/vendure/src/api/index.ts @@ -49,6 +49,8 @@ export const provider = { config, operations } export type Provider = typeof provider +export type VendureAPI

= CommerceAPI

+ export function getCommerceApi

( customProvider: P = provider as any ): CommerceAPI

{ diff --git a/packages/vendure/src/api/operations/login.ts b/packages/vendure/src/api/operations/login.ts index 494af1f75..bcfa471ab 100644 --- a/packages/vendure/src/api/operations/login.ts +++ b/packages/vendure/src/api/operations/login.ts @@ -1,4 +1,3 @@ -import type { ServerResponse } from 'http' import type { OperationContext, OperationOptions, @@ -15,14 +14,14 @@ export default function loginOperation({ async function login(opts: { variables: T['variables'] config?: Partial - res: ServerResponse + res: Response }): Promise async function login( opts: { variables: T['variables'] config?: Partial - res: ServerResponse + res: Response } & OperationOptions ): Promise @@ -34,7 +33,7 @@ export default function loginOperation({ }: { query?: string variables: T['variables'] - res: ServerResponse + res: Response config?: Partial }): Promise { const config = commerce.getConfig(cfg) diff --git a/packages/vendure/src/api/utils/fetch-graphql-api.ts b/packages/vendure/src/api/utils/fetch-graphql-api.ts index 97aaba178..f8377f7ae 100644 --- a/packages/vendure/src/api/utils/fetch-graphql-api.ts +++ b/packages/vendure/src/api/utils/fetch-graphql-api.ts @@ -1,20 +1,18 @@ import { FetcherError } from '@vercel/commerce/utils/errors' import type { GraphQLFetcher } from '@vercel/commerce/api' import { getCommerceApi } from '../' -import fetch from './fetch' const fetchGraphqlApi: GraphQLFetcher = async ( query: string, - { variables, preview } = {}, - fetchOptions + { variables } = {}, + headers?: HeadersInit ) => { const config = getCommerceApi().getConfig() const res = await fetch(config.commerceUrl, { - ...fetchOptions, method: 'POST', headers: { - ...fetchOptions?.headers, + ...headers, 'Content-Type': 'application/json', }, body: JSON.stringify({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d6135ebc3..02515ed23 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,9 +14,11 @@ importers: packages/bigcommerce: specifiers: + '@cfworker/uuid': ^1.12.4 '@taskr/clear': ^1.1.0 '@taskr/esnext': ^1.1.0 '@taskr/watch': ^1.1.0 + '@tsndr/cloudflare-worker-jwt': ^2.1.0 '@types/cookie': ^0.4.1 '@types/jsonwebtoken': ^8.5.7 '@types/lodash.debounce': ^4.0.6 @@ -24,29 +26,29 @@ importers: '@types/node-fetch': ^2.6.2 '@types/react': ^18.0.14 '@vercel/commerce': workspace:* - '@vercel/fetch': ^6.2.0 cookie: ^0.4.1 immutability-helper: ^3.1.1 + js-cookie: ^3.0.1 jsonwebtoken: ^8.5.1 lint-staged: ^12.1.7 lodash.debounce: ^4.0.8 next: ^12.0.8 - node-fetch: ^2.6.7 prettier: ^2.5.1 react: ^18.2.0 react-dom: ^18.2.0 taskr: ^1.1.0 taskr-swc: ^0.0.1 typescript: ^4.7.4 - uuidv4: ^6.2.12 + uuidv4: ^6.2.13 dependencies: + '@cfworker/uuid': 1.12.4 + '@tsndr/cloudflare-worker-jwt': 2.1.0 '@vercel/commerce': link:../commerce - '@vercel/fetch': 6.2.0_wbqoqouw2iimn65bqgaw3lwmza cookie: 0.4.2 immutability-helper: 3.1.1 + js-cookie: 3.0.1 jsonwebtoken: 8.5.1 lodash.debounce: 4.0.8 - node-fetch: 2.6.7 uuidv4: 6.2.13 devDependencies: '@taskr/clear': 1.1.0 @@ -76,13 +78,12 @@ importers: '@types/node': ^17.0.8 '@types/node-fetch': 2.6.2 '@types/react': ^18.0.14 - '@vercel/fetch': ^6.2.0 + '@vercel/edge': ^0.0.4 deepmerge: ^4.2.2 import-cwd: ^3.0.0 js-cookie: ^3.0.1 lint-staged: ^12.1.7 next: ^12.0.8 - node-fetch: ^2.6.7 prettier: ^2.5.1 react: ^18.2.0 react-dom: ^18.2.0 @@ -90,13 +91,12 @@ importers: taskr: ^1.1.0 taskr-swc: ^0.0.1 typescript: ^4.7.4 - zod: ^3.19.0 + zod: ^3.19.1 dependencies: - '@vercel/fetch': 6.2.0_wbqoqouw2iimn65bqgaw3lwmza + '@vercel/edge': 0.0.4 deepmerge: 4.2.2 import-cwd: 3.0.0 js-cookie: 3.0.1 - node-fetch: 2.6.7 swr: 1.3.0_react@18.2.0 zod: 3.19.1 devDependencies: @@ -183,12 +183,10 @@ importers: '@types/node-fetch': 2.6.2 '@types/react': ^18.0.14 '@vercel/commerce': workspace:* - '@vercel/fetch': ^6.2.0 graphql: ^16.0.0 lint-staged: ^12.1.7 lodash.debounce: ^4.0.8 next: ^12.0.8 - node-fetch: ^2.6.7 prettier: ^2.5.1 react: ^18.2.0 react-dom: ^18.2.0 @@ -198,9 +196,7 @@ importers: typescript: ^4.7.4 dependencies: '@vercel/commerce': link:../commerce - '@vercel/fetch': 6.2.0_wbqoqouw2iimn65bqgaw3lwmza lodash.debounce: 4.0.8 - node-fetch: 2.6.7 devDependencies: '@graphql-codegen/cli': 2.12.0_5nhrphdxro5xxa2f5vmz5votda '@graphql-codegen/schema-ast': 2.5.1_graphql@16.6.0 @@ -233,10 +229,8 @@ importers: '@types/node-fetch': 2.6.2 '@types/react': ^18.0.14 '@vercel/commerce': workspace:* - '@vercel/fetch': ^6.2.0 lint-staged: ^12.1.7 next: ^12.0.8 - node-fetch: ^2.6.7 prettier: ^2.5.1 react: ^18.2.0 react-dom: ^18.2.0 @@ -245,8 +239,6 @@ importers: typescript: ^4.7.4 dependencies: '@vercel/commerce': link:../commerce - '@vercel/fetch': 6.2.0_wbqoqouw2iimn65bqgaw3lwmza - node-fetch: 2.6.7 devDependencies: '@taskr/clear': 1.1.0 '@taskr/esnext': 1.1.0 @@ -274,26 +266,20 @@ importers: '@types/node-fetch': ^2.6.2 '@types/react': ^18.0.14 '@vercel/commerce': workspace:* - '@vercel/fetch': ^6.2.0 cookie: ^0.4.1 lint-staged: ^12.1.7 lodash.debounce: ^4.0.8 next: ^12.0.8 - node-fetch: ^2.6.7 prettier: ^2.5.1 react: ^18.2.0 react-dom: ^18.2.0 - stripe: ^8.197.0 taskr: ^1.1.0 taskr-swc: ^0.0.1 typescript: ^4.7.4 dependencies: '@vercel/commerce': link:../commerce - '@vercel/fetch': 6.2.0_wbqoqouw2iimn65bqgaw3lwmza cookie: 0.4.2 lodash.debounce: 4.0.8 - node-fetch: 2.6.7 - stripe: 8.222.0 devDependencies: '@taskr/clear': 1.1.0 '@taskr/esnext': 1.1.0 @@ -327,13 +313,11 @@ importers: '@types/node-fetch': ^2.6.2 '@types/react': ^18.0.14 '@vercel/commerce': workspace:* - '@vercel/fetch': ^6.2.0 graphql: ^16.0.0 js-cookie: ^3.0.1 lint-staged: ^12.1.7 lodash.debounce: ^4.0.8 next: ^12.0.8 - node-fetch: ^2.6.7 prettier: ^2.5.1 react: ^18.2.0 react-dom: ^18.2.0 @@ -343,10 +327,8 @@ importers: typescript: ^4.7.4 dependencies: '@vercel/commerce': link:../commerce - '@vercel/fetch': 6.2.0_wbqoqouw2iimn65bqgaw3lwmza js-cookie: 3.0.1 lodash.debounce: 4.0.8 - node-fetch: 2.6.7 devDependencies: '@graphql-codegen/cli': 2.12.0_5nhrphdxro5xxa2f5vmz5votda '@graphql-codegen/schema-ast': 2.5.1_graphql@16.6.0 @@ -380,11 +362,9 @@ importers: '@types/node-fetch': ^2.6.2 '@types/react': ^18.0.14 '@vercel/commerce': workspace:* - '@vercel/fetch': ^6.2.0 commerce-sdk: ^2.7.0 lint-staged: ^12.1.7 next: ^12.0.8 - node-fetch: ^2.6.7 prettier: ^2.5.1 react: ^18.2.0 react-dom: ^18.2.0 @@ -393,9 +373,7 @@ importers: typescript: ^4.7.4 dependencies: '@vercel/commerce': link:../commerce - '@vercel/fetch': 6.2.0_wbqoqouw2iimn65bqgaw3lwmza commerce-sdk: 2.8.0 - node-fetch: 2.6.7 devDependencies: '@taskr/clear': 1.1.0 '@taskr/esnext': 1.1.0 @@ -427,14 +405,12 @@ importers: '@types/node-fetch': 2.6.2 '@types/react': ^18.0.14 '@vercel/commerce': workspace:* - '@vercel/fetch': ^6.2.0 dotenv: ^16.0.1 graphql: ^16.0.0 js-cookie: ^3.0.1 lint-staged: ^13.0.3 lodash.debounce: ^4.0.8 next: ^12.2.1 - node-fetch: ^2.6.7 prettier: ^2.7.1 react: ^18.2.0 react-dom: ^18.2.0 @@ -443,10 +419,8 @@ importers: typescript: ^4.3.4 dependencies: '@vercel/commerce': link:../commerce - '@vercel/fetch': 6.2.0_wbqoqouw2iimn65bqgaw3lwmza js-cookie: 3.0.1 lodash.debounce: 4.0.8 - node-fetch: 2.6.7 devDependencies: '@graphql-codegen/cli': 2.7.0_gholt4t4onvjnzhsre2mzmeyhy '@graphql-codegen/schema-ast': 2.5.1_graphql@16.6.0 @@ -483,12 +457,10 @@ importers: '@types/node-fetch': ^2.6.2 '@types/react': ^18.0.14 '@vercel/commerce': workspace:* - '@vercel/fetch': ^6.2.0 js-cookie: ^3.0.1 lint-staged: ^12.1.7 lodash.debounce: ^4.0.8 next: ^12.0.8 - node-fetch: ^2.6.7 prettier: ^2.5.1 react: ^18.2.0 react-dom: ^18.2.0 @@ -497,12 +469,10 @@ importers: taskr-swc: ^0.0.1 typescript: ^4.7.4 dependencies: - '@spree/storefront-api-v2-sdk': 5.1.4_node-fetch@2.6.7 + '@spree/storefront-api-v2-sdk': 5.1.4 '@vercel/commerce': link:../commerce - '@vercel/fetch': 6.2.0_wbqoqouw2iimn65bqgaw3lwmza js-cookie: 3.0.1 lodash.debounce: 4.0.8 - node-fetch: 2.6.7 swr: 1.3.0_react@18.2.0 devDependencies: '@taskr/clear': 1.1.0 @@ -533,12 +503,10 @@ importers: '@types/node-fetch': ^2.6.2 '@types/react': ^18.0.14 '@vercel/commerce': workspace:* - '@vercel/fetch': ^6.2.0 js-cookie: ^3.0.1 lint-staged: ^12.1.7 lodash.debounce: ^4.0.8 next: ^12.0.8 - node-fetch: ^2.6.7 prettier: ^2.5.1 react: ^18.2.0 react-dom: ^18.2.0 @@ -548,10 +516,8 @@ importers: typescript: ^4.7.4 dependencies: '@vercel/commerce': link:../commerce - '@vercel/fetch': 6.2.0_wbqoqouw2iimn65bqgaw3lwmza js-cookie: 3.0.1 lodash.debounce: 4.0.8 - node-fetch: 2.6.7 swell-js: 4.0.0-next.0 devDependencies: '@taskr/clear': 1.1.0 @@ -592,11 +558,9 @@ importers: '@types/node-fetch': ^2.6.2 '@types/react': ^18.0.14 '@vercel/commerce': workspace:* - '@vercel/fetch': ^6.2.0 graphql: ^16.0.0 lint-staged: ^12.1.7 next: ^12.0.8 - node-fetch: ^2.6.7 prettier: ^2.5.1 react: ^18.2.0 react-dom: ^18.2.0 @@ -605,8 +569,6 @@ importers: typescript: ^4.7.4 dependencies: '@vercel/commerce': link:../commerce - '@vercel/fetch': 6.2.0_wbqoqouw2iimn65bqgaw3lwmza - node-fetch: 2.6.7 devDependencies: '@graphql-codegen/cli': 2.7.0_fte77dov2vin5jxmf6euzzc57i '@graphql-codegen/schema-ast': 2.5.1_graphql@16.6.0 @@ -1347,6 +1309,10 @@ packages: to-fast-properties: 2.0.0 dev: true + /@cfworker/uuid/1.12.4: + resolution: {integrity: sha512-aw4lxCT4NLbGqlTScWPtPrZrg4TO6HCZCnNUAeC4yTOvkXjJGdJP6S5NCY9vtP2FruLIC+vYCvUP98fHXtF7kg==} + dev: false + /@chec/commerce.js/2.8.0: resolution: {integrity: sha512-OPBphT/hU33iDp52zzYOqz/oSXLhEuhGVUg2UNvYtmBW4eCNmtsM0dqW0+wu+6K0d6fZojurCBdVQMKb2R7l3g==} dependencies: @@ -2888,7 +2854,7 @@ packages: engines: {node: '>=6'} dev: true - /@spree/storefront-api-v2-sdk/5.1.4_node-fetch@2.6.7: + /@spree/storefront-api-v2-sdk/5.1.4: resolution: {integrity: sha512-KvAVQ9wDAy+2EajiEGoFmw3iZRi9xERR/wKS2z+h2BaQsHMEJhe/xh06lFecEu9RKH4/k3m2dqrijyNzWLJ+Gw==} engines: {node: '>=14.17.0'} peerDependencies: @@ -2899,8 +2865,6 @@ packages: optional: true node-fetch: optional: true - dependencies: - node-fetch: 2.6.7 dev: false /@swc/core-android-arm-eabi/1.3.0: @@ -3127,14 +3091,8 @@ packages: resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} dev: true - /@types/async-retry/1.2.1: - resolution: {integrity: sha512-yMQ6CVgICWtyFNBqJT3zqOc+TnqqEPLo4nKJNPFwcialiylil38Ie6q1ENeFTjvaLOkVim9K5LisHgAKJWidGQ==} - dev: false - - /@types/async-retry/1.4.5: - resolution: {integrity: sha512-YrdjSD+yQv7h6d5Ip+PMxh3H6ZxKyQk0Ts+PvaNRInxneG9PFVZjFg77ILAN+N6qYf7g4giSJ1l+ZjQ1zeegvA==} - dependencies: - '@types/retry': 0.12.2 + /@tsndr/cloudflare-worker-jwt/2.1.0: + resolution: {integrity: sha512-U8yLexXd7ytstTEp7hEbwQkiMqjxEWz4OfjGju3PhBbNFdi9lH+2kdVB+QQU0g72YjQJZ9UCKzhy0lYzdekLQA==} dev: false /@types/body-scroll-lock/3.1.0: @@ -3199,22 +3157,16 @@ packages: resolution: {integrity: sha512-evMDG1bC4rgQg4ku9tKpuMh5iBNEwNa3tf9zRHdP1qlv+1WUg44xat4IxCE14gIpZRGUUWAx2VhItCZc25NfMA==} dev: true - /@types/lru-cache/4.1.1: - resolution: {integrity: sha512-8mNEUG6diOrI6pMqOHrHPDBB1JsrpedeMK9AWGzVCQ7StRRribiT9BRvUmF8aUws9iBbVlgVekOT5Sgzc1MTKw==} - dev: false - /@types/node-fetch/2.6.2: resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} dependencies: '@types/node': 17.0.45 form-data: 3.0.1 - - /@types/node/10.12.18: - resolution: {integrity: sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==} - dev: false + dev: true /@types/node/17.0.45: resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + dev: true /@types/node/18.7.18: resolution: {integrity: sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==} @@ -3246,10 +3198,6 @@ packages: '@types/node': 18.7.18 dev: true - /@types/retry/0.12.2: - resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} - dev: false - /@types/scheduler/0.16.2: resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} @@ -3260,7 +3208,7 @@ packages: /@types/ws/8.5.3: resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==} dependencies: - '@types/node': 17.0.45 + '@types/node': 18.7.18 dev: true /@typescript-eslint/parser/5.37.0_4brgkhw6cq4me3drk3kxrpb2mm: @@ -3325,43 +3273,8 @@ packages: eslint-visitor-keys: 3.3.0 dev: true - /@vercel/fetch-cached-dns/2.1.0_node-fetch@2.6.7: - resolution: {integrity: sha512-dIQWF+bG2EOYeCCCeT3E77qZZa7VgW2quEKw4k8/keeoD8lRMjiNi//Ww7LJ8wXecfv7XXtprwN5uLLLGo/ktg==} - peerDependencies: - node-fetch: ^2.6.1 - dependencies: - '@types/node-fetch': 2.6.2 - '@zeit/dns-cached-resolve': 2.1.2 - node-fetch: 2.6.7 - dev: false - - /@vercel/fetch-retry/5.1.3_node-fetch@2.6.7: - resolution: {integrity: sha512-UIbFc4VsEZHOr6dWuE+kxY4NxnOLXFMCWm0fSKRRHUEtrIzaJLzHpWk2QskCXTSzFgFvhkLAvSrBK2XZg7NSzg==} - peerDependencies: - node-fetch: ^2.6.7 - dependencies: - async-retry: 1.3.3 - debug: 4.3.4 - node-fetch: 2.6.7 - transitivePeerDependencies: - - supports-color - dev: false - - /@vercel/fetch/6.2.0_wbqoqouw2iimn65bqgaw3lwmza: - resolution: {integrity: sha512-MU+Mzh06NIAXxwdnyHmBFg+/lTKBbzDkCSNhAwWTFJ4rHuBc4pHc8E6XP+qnwqaWugjOBQgFfQCGDLnV820c9A==} - peerDependencies: - '@types/node-fetch': ^2.6.1 - node-fetch: ^2.6.7 - dependencies: - '@types/async-retry': 1.4.5 - '@types/node-fetch': 2.6.2 - '@vercel/fetch-cached-dns': 2.1.0_node-fetch@2.6.7 - '@vercel/fetch-retry': 5.1.3_node-fetch@2.6.7 - agentkeepalive: 4.2.1 - debug: 4.3.4 - node-fetch: 2.6.7 - transitivePeerDependencies: - - supports-color + /@vercel/edge/0.0.4: + resolution: {integrity: sha512-KzZuSib25DsYjLa7UiMhAWfhfE9Oby1T90M0mkYMjCNK71i9vvEKRQXnhs3htmyNyid11KyI4wMEnIr7M9DqAA==} dev: false /@whatwg-node/fetch/0.3.2: @@ -3396,16 +3309,6 @@ packages: - encoding dev: true - /@zeit/dns-cached-resolve/2.1.2: - resolution: {integrity: sha512-A/5gbBskKPETTBqHwvlaW1Ri2orO62yqoFoXdxna1SQ7A/lXjpWgpJ1wdY3IQEcz5LydpS4sJ8SzI2gFyyLEhg==} - dependencies: - '@types/async-retry': 1.2.1 - '@types/lru-cache': 4.1.1 - '@types/node': 10.12.18 - async-retry: 1.2.3 - lru-cache: 5.1.1 - dev: false - /abort-controller/3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -3712,20 +3615,9 @@ packages: resolution: {integrity: sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==} dev: true - /async-retry/1.2.3: - resolution: {integrity: sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==} - dependencies: - retry: 0.12.0 - dev: false - - /async-retry/1.3.3: - resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} - dependencies: - retry: 0.13.1 - dev: false - /asynckit/0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: true /atob/2.1.2: resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} @@ -4281,6 +4173,7 @@ packages: engines: {node: '>= 0.8'} dependencies: delayed-stream: 1.0.0 + dev: true /commander/6.2.1: resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} @@ -4600,6 +4493,7 @@ packages: /delayed-stream/1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + dev: true /denque/1.5.1: resolution: {integrity: sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==} @@ -5407,6 +5301,7 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 + dev: true /formdata-node/4.4.1: resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} @@ -6858,12 +6753,6 @@ packages: engines: {node: '>=8'} dev: true - /lru-cache/5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - dependencies: - yallist: 3.1.1 - dev: false - /lru-cache/6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -7003,12 +6892,14 @@ packages: /mime-db/1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + dev: true /mime-types/2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} dependencies: mime-db: 1.52.0 + dev: true /mimic-fn/1.2.0: resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==} @@ -8958,14 +8849,6 @@ packages: engines: {node: '>=8'} dev: true - /stripe/8.222.0: - resolution: {integrity: sha512-hrA79fjmN2Eb6K3kxkDzU4ODeVGGjXQsuVaAPSUro6I9MM3X+BvIsVqdphm3BXWfimAGFvUqWtPtHy25mICY1w==} - engines: {node: ^8.1 || >=10.*} - dependencies: - '@types/node': 17.0.45 - qs: 6.11.0 - dev: false - /styled-jsx/5.0.6_react@18.2.0: resolution: {integrity: sha512-xOeROtkK5MGMDimBQ3J6iPId8q0t/BDoG5XN6oKkZClVz9ISF/hihN8OCn2LggMU6N32aXnrXBdn3auSqNS9fA==} engines: {node: '>= 12.0.0'} @@ -9776,10 +9659,6 @@ packages: engines: {node: '>=10'} dev: true - /yallist/3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - dev: false - /yallist/4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} diff --git a/site/components/auth/LoginView.tsx b/site/components/auth/LoginView.tsx index 3c8faef7c..c7d399f94 100644 --- a/site/components/auth/LoginView.tsx +++ b/site/components/auth/LoginView.tsx @@ -33,8 +33,12 @@ const LoginView: React.FC = () => { }) setLoading(false) closeModal() - } catch (e: any) { - setMessage(e.errors[0].message) + } catch ({ errors }) { + if (errors instanceof Array) { + setMessage(errors.map((e: any) => e.message).join('
')) + } else { + setMessage('Unexpected error') + } setLoading(false) setDisabled(false) } diff --git a/site/components/auth/SignUpView.tsx b/site/components/auth/SignUpView.tsx index 5744cc80f..fa71bf95d 100644 --- a/site/components/auth/SignUpView.tsx +++ b/site/components/auth/SignUpView.tsx @@ -41,13 +41,14 @@ const SignUpView: FC = () => { setLoading(false) closeModal() } catch ({ errors }) { + console.error(errors) if (errors instanceof Array) { - setMessage(errors[0].message) + setMessage(errors.map((e: any) => e.message).join('
')) } else { setMessage('Unexpected error') - console.log(errors) } setLoading(false) + setDisabled(false) } } @@ -75,7 +76,12 @@ const SignUpView: FC = () => {

{message && ( -
{message}
+
)} diff --git a/site/components/common/UserNav/UserNav.tsx b/site/components/common/UserNav/UserNav.tsx index 039296e0d..0f119de69 100644 --- a/site/components/common/UserNav/UserNav.tsx +++ b/site/components/common/UserNav/UserNav.tsx @@ -31,7 +31,7 @@ const UserNav: React.FC<{ openSidebar, } = useUI() - const itemsCount = data?.lineItems.reduce(countItem, 0) ?? 0 + const itemsCount = data?.lineItems?.reduce(countItem, 0) ?? 0 const DropdownTrigger = isCustomerLoggedIn ? DropdownTriggerInst : React.Fragment diff --git a/site/components/product/ProductSidebar/ProductSidebar.tsx b/site/components/product/ProductSidebar/ProductSidebar.tsx index e549ed698..67c3dab9f 100644 --- a/site/components/product/ProductSidebar/ProductSidebar.tsx +++ b/site/components/product/ProductSidebar/ProductSidebar.tsx @@ -9,6 +9,7 @@ import { selectDefaultOptionFromProduct, SelectedOptions, } from '../helpers' +import ErrorMessage from '@components/ui/ErrorMessage' interface ProductSidebarProps { product: Product @@ -19,6 +20,7 @@ const ProductSidebar: FC = ({ product, className }) => { const addItem = useAddItem() const { openSidebar, setSidebarView } = useUI() const [loading, setLoading] = useState(false) + const [error, setError] = useState(null) const [selectedOptions, setSelectedOptions] = useState({}) useEffect(() => { @@ -28,6 +30,7 @@ const ProductSidebar: FC = ({ product, className }) => { const variant = getProductVariant(product, selectedOptions) const addToCart = async () => { setLoading(true) + setError(null) try { await addItem({ productId: String(product.id), @@ -38,6 +41,13 @@ const ProductSidebar: FC = ({ product, className }) => { setLoading(false) } catch (err) { setLoading(false) + if (err instanceof Error) { + console.error(err) + setError({ + ...err, + message: 'Could not add item to cart. Please try again.', + }) + } } } @@ -57,6 +67,7 @@ const ProductSidebar: FC = ({ product, className }) => {
36 reviews
+ {error && } {process.env.COMMERCE_CART_ENABLED && (
+ return } const handleClick = (event: any, filter: string) => { diff --git a/site/components/ui/ErrorMessage/ErrorMessage.tsx b/site/components/ui/ErrorMessage/ErrorMessage.tsx new file mode 100644 index 000000000..3c90764d0 --- /dev/null +++ b/site/components/ui/ErrorMessage/ErrorMessage.tsx @@ -0,0 +1,35 @@ +import type { FC } from 'react' +import cn from 'clsx' + +interface ErrorMessageProps { + error: { + message: string + code?: string + errors?: { + message: string + }[] + } + className?: string +} + +const ErrorMessages: FC = ({ error, className }) => { + return ( +
+ {error.message} + {error.errors && error.errors?.length > 0 && ( +
    + {error.errors.map(({ message }, index) => ( +
  • {message}
  • + ))} +
+ )} +
+ ) +} + +export default ErrorMessages diff --git a/site/components/ui/ErrorMessage/index.ts b/site/components/ui/ErrorMessage/index.ts new file mode 100644 index 000000000..e4f7f45e4 --- /dev/null +++ b/site/components/ui/ErrorMessage/index.ts @@ -0,0 +1 @@ +export { default } from './ErrorMessage' diff --git a/site/lib/api/commerce.ts b/site/lib/api/commerce.ts index 499137004..db81f6543 100644 --- a/site/lib/api/commerce.ts +++ b/site/lib/api/commerce.ts @@ -1,3 +1,2 @@ import { getCommerceApi } from '@framework/api' - export default getCommerceApi() diff --git a/site/next.config.js b/site/next.config.js index 744c4cf0e..0b21164a3 100644 --- a/site/next.config.js +++ b/site/next.config.js @@ -18,7 +18,7 @@ module.exports = withCommerceConfig({ return [ (isBC || isShopify || isSwell || isVendure || isSaleor) && { source: '/checkout', - destination: '/api/checkout', + destination: '/api/commerce/checkout', }, // The logout is also an action so this route is not required, but it's also another way // you can allow a logout! diff --git a/site/pages/api/cart.ts b/site/pages/api/cart.ts deleted file mode 100644 index 642891107..000000000 --- a/site/pages/api/cart.ts +++ /dev/null @@ -1,4 +0,0 @@ -import cartApi from '@framework/api/endpoints/cart' -import commerce from '@lib/api/commerce' - -export default cartApi(commerce) diff --git a/site/pages/api/catalog/products.ts b/site/pages/api/catalog/products.ts deleted file mode 100644 index 631bfd516..000000000 --- a/site/pages/api/catalog/products.ts +++ /dev/null @@ -1,4 +0,0 @@ -import productsApi from '@framework/api/endpoints/catalog/products' -import commerce from '@lib/api/commerce' - -export default productsApi(commerce) diff --git a/site/pages/api/checkout.ts b/site/pages/api/checkout.ts deleted file mode 100644 index 7bf0fd9aa..000000000 --- a/site/pages/api/checkout.ts +++ /dev/null @@ -1,4 +0,0 @@ -import checkoutApi from '@framework/api/endpoints/checkout' -import commerce from '@lib/api/commerce' - -export default checkoutApi(commerce) diff --git a/site/pages/api/commerce/[[...commerce]].ts b/site/pages/api/commerce/[[...commerce]].ts new file mode 100644 index 000000000..956555630 --- /dev/null +++ b/site/pages/api/commerce/[[...commerce]].ts @@ -0,0 +1,8 @@ +import commerce from '@lib/api/commerce' +import endpoints from '@framework/api/endpoints' + +// export const config = { +// runtime: 'experimental-edge', +// } + +export default endpoints(commerce) diff --git a/site/pages/api/customer/address.ts b/site/pages/api/customer/address.ts deleted file mode 100644 index 5815ea462..000000000 --- a/site/pages/api/customer/address.ts +++ /dev/null @@ -1,4 +0,0 @@ -import customerAddressApi from '@framework/api/endpoints/customer/address' -import commerce from '@lib/api/commerce' - -export default customerAddressApi(commerce) diff --git a/site/pages/api/customer/card.ts b/site/pages/api/customer/card.ts deleted file mode 100644 index 6f88b8c74..000000000 --- a/site/pages/api/customer/card.ts +++ /dev/null @@ -1,4 +0,0 @@ -import customerCardApi from '@framework/api/endpoints/customer/card' -import commerce from '@lib/api/commerce' - -export default customerCardApi(commerce) diff --git a/site/pages/api/customer/index.ts b/site/pages/api/customer/index.ts deleted file mode 100644 index 0c86e76e5..000000000 --- a/site/pages/api/customer/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import customerApi from '@framework/api/endpoints/customer' -import commerce from '@lib/api/commerce' - -export default customerApi(commerce) diff --git a/site/pages/api/login.ts b/site/pages/api/login.ts deleted file mode 100644 index 9d0b6ae57..000000000 --- a/site/pages/api/login.ts +++ /dev/null @@ -1,4 +0,0 @@ -import loginApi from '@framework/api/endpoints/login' -import commerce from '@lib/api/commerce' - -export default loginApi(commerce) diff --git a/site/pages/api/logout.ts b/site/pages/api/logout.ts deleted file mode 100644 index 0cf0fc4d2..000000000 --- a/site/pages/api/logout.ts +++ /dev/null @@ -1,4 +0,0 @@ -import logoutApi from '@framework/api/endpoints/logout' -import commerce from '@lib/api/commerce' - -export default logoutApi(commerce) diff --git a/site/pages/api/signup.ts b/site/pages/api/signup.ts deleted file mode 100644 index e19d67ee8..000000000 --- a/site/pages/api/signup.ts +++ /dev/null @@ -1,4 +0,0 @@ -import singupApi from '@framework/api/endpoints/signup' -import commerce from '@lib/api/commerce' - -export default singupApi(commerce) diff --git a/site/pages/api/wishlist.ts b/site/pages/api/wishlist.ts deleted file mode 100644 index 3b9681209..000000000 --- a/site/pages/api/wishlist.ts +++ /dev/null @@ -1,4 +0,0 @@ -import wishlistApi from '@framework/api/endpoints/wishlist' -import commerce from '@lib/api/commerce' - -export default wishlistApi(commerce) diff --git a/site/pages/product/[slug].tsx b/site/pages/product/[slug].tsx index 439de6d1d..9d8d153e4 100644 --- a/site/pages/product/[slug].tsx +++ b/site/pages/product/[slug].tsx @@ -22,7 +22,6 @@ export async function getStaticProps({ config, preview, }) - const allProductsPromise = commerce.getAllProducts({ variables: { first: 4 }, config,