mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 20:26:49 +00:00
Save token to cookie
This commit is contained in:
@@ -9,7 +9,7 @@ import { formatCart } from '../../utils/cart'
|
||||
const addItem: CartEndpoint['handlers']['addItem'] = async ({
|
||||
res,
|
||||
body: { cartId, item },
|
||||
config: { restFetch, cartCookie },
|
||||
config: { restBuyerFetch, cartCookie, tokenCookie },
|
||||
}) => {
|
||||
// Return an error if no item is present
|
||||
if (!item) {
|
||||
@@ -19,52 +19,74 @@ const addItem: CartEndpoint['handlers']['addItem'] = async ({
|
||||
})
|
||||
}
|
||||
|
||||
// Store token
|
||||
let token
|
||||
|
||||
// Set the quantity if not present
|
||||
if (!item.quantity) item.quantity = 1
|
||||
|
||||
// Create an order if it doesn't exist
|
||||
if (!cartId) {
|
||||
cartId = await restFetch('POST', `/orders/Outgoing`, {}).then(
|
||||
(response: { ID: string }) => response.ID
|
||||
)
|
||||
}
|
||||
const { ID, meta } = await restBuyerFetch(
|
||||
'POST',
|
||||
`/orders/Outgoing`,
|
||||
{}
|
||||
).then((response: { ID: string; meta: { token: string } }) => response)
|
||||
|
||||
// Set the cart cookie
|
||||
res.setHeader(
|
||||
'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',
|
||||
})
|
||||
)
|
||||
// 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',
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
// Store specs
|
||||
let specs: RawVariant['Specs'] = []
|
||||
|
||||
// If a variant is present, fetch its specs
|
||||
if (item.variantId) {
|
||||
specs = await restFetch(
|
||||
specs = await restBuyerFetch(
|
||||
'GET',
|
||||
`/me/products/${item.productId}/variants/${item.variantId}`
|
||||
`/me/products/${item.productId}/variants/${item.variantId}`,
|
||||
null,
|
||||
{ token }
|
||||
).then((res: RawVariant) => res.Specs)
|
||||
}
|
||||
|
||||
// Add the item to the order
|
||||
await restFetch('POST', `/orders/Outgoing/${cartId}/lineitems`, {
|
||||
ProductID: item.productId,
|
||||
Quantity: item.quantity,
|
||||
Specs: specs,
|
||||
})
|
||||
await restBuyerFetch(
|
||||
'POST',
|
||||
`/orders/Outgoing/${cartId}/lineitems`,
|
||||
{
|
||||
ProductID: item.productId,
|
||||
Quantity: item.quantity,
|
||||
Specs: specs,
|
||||
},
|
||||
{ token }
|
||||
)
|
||||
|
||||
// Get cart
|
||||
const [cart, lineItems] = await Promise.all([
|
||||
restFetch('GET', `/orders/Outgoing/${cartId}`),
|
||||
restFetch('GET', `/orders/Outgoing/${cartId}/lineitems`).then(
|
||||
(response: { Items: OrdercloudLineItem[] }) => response.Items
|
||||
),
|
||||
restBuyerFetch('GET', `/orders/Outgoing/${cartId}`, null, { token }),
|
||||
restBuyerFetch('GET', `/orders/Outgoing/${cartId}/lineitems`, null, {
|
||||
token,
|
||||
}).then((response: { Items: OrdercloudLineItem[] }) => response.Items),
|
||||
])
|
||||
|
||||
// Format cart
|
||||
|
@@ -7,9 +7,10 @@ import { formatCart } from '../../utils/cart'
|
||||
|
||||
// Return current cart info
|
||||
const getCart: CartEndpoint['handlers']['getCart'] = async ({
|
||||
req,
|
||||
res,
|
||||
body: { cartId },
|
||||
config: { restFetch, cartCookie },
|
||||
config: { restBuyerFetch, cartCookie, tokenCookie },
|
||||
}) => {
|
||||
if (!cartId) {
|
||||
return res.status(400).json({
|
||||
@@ -19,13 +20,23 @@ const getCart: CartEndpoint['handlers']['getCart'] = async ({
|
||||
}
|
||||
|
||||
try {
|
||||
// Get token from cookies
|
||||
const token = req.cookies[tokenCookie]
|
||||
|
||||
// Get cart
|
||||
const cart = await restFetch('GET', `/orders/Outgoing/${cartId}`)
|
||||
const cart = await restBuyerFetch(
|
||||
'GET',
|
||||
`/orders/Outgoing/${cartId}`,
|
||||
null,
|
||||
{ token }
|
||||
)
|
||||
|
||||
// Get line items
|
||||
const lineItems = await restFetch(
|
||||
const lineItems = await restBuyerFetch(
|
||||
'GET',
|
||||
`/orders/Outgoing/${cartId}/lineitems`
|
||||
`/orders/Outgoing/${cartId}/lineitems`,
|
||||
null,
|
||||
{ token }
|
||||
).then((response: { Items: OrdercloudLineItem[] }) => response.Items)
|
||||
|
||||
// Format cart
|
||||
@@ -34,14 +45,17 @@ const getCart: CartEndpoint['handlers']['getCart'] = async ({
|
||||
// Return cart and errors
|
||||
res.status(200).json({ data: formattedCart, errors: [] })
|
||||
} catch (error) {
|
||||
// Reset cart cookie
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
// Reset cart and token cookie
|
||||
res.setHeader('Set-Cookie', [
|
||||
serialize(cartCookie, cartId, {
|
||||
maxAge: -1,
|
||||
path: '/',
|
||||
})
|
||||
)
|
||||
}),
|
||||
serialize(tokenCookie, cartId, {
|
||||
maxAge: -1,
|
||||
path: '/',
|
||||
}),
|
||||
])
|
||||
|
||||
// Return empty cart
|
||||
res.status(200).json({ data: null, errors: [] })
|
||||
|
@@ -4,9 +4,10 @@ import { formatCart } from '../../utils/cart'
|
||||
import { OrdercloudLineItem } from '../../../types/cart'
|
||||
|
||||
const removeItem: CartEndpoint['handlers']['removeItem'] = async ({
|
||||
req,
|
||||
res,
|
||||
body: { cartId, itemId },
|
||||
config: { restFetch },
|
||||
config: { restBuyerFetch, tokenCookie },
|
||||
}) => {
|
||||
if (!cartId || !itemId) {
|
||||
return res.status(400).json({
|
||||
@@ -15,18 +16,23 @@ const removeItem: CartEndpoint['handlers']['removeItem'] = async ({
|
||||
})
|
||||
}
|
||||
|
||||
// Get token from cookies
|
||||
const token = req.cookies[tokenCookie]
|
||||
|
||||
// Remove the item to the order
|
||||
await restFetch(
|
||||
await restBuyerFetch(
|
||||
'DELETE',
|
||||
`/orders/Outgoing/${cartId}/lineitems/${itemId}`
|
||||
`/orders/Outgoing/${cartId}/lineitems/${itemId}`,
|
||||
null,
|
||||
{ token }
|
||||
)
|
||||
|
||||
// Get cart
|
||||
const [cart, lineItems] = await Promise.all([
|
||||
restFetch('GET', `/orders/Outgoing/${cartId}`),
|
||||
restFetch('GET', `/orders/Outgoing/${cartId}/lineitems`).then(
|
||||
(response: { Items: OrdercloudLineItem[] }) => response.Items
|
||||
),
|
||||
restBuyerFetch('GET', `/orders/Outgoing/${cartId}`, null, { token }),
|
||||
restBuyerFetch('GET', `/orders/Outgoing/${cartId}/lineitems`, null, {
|
||||
token,
|
||||
}).then((response: { Items: OrdercloudLineItem[] }) => response.Items),
|
||||
])
|
||||
|
||||
// Format cart
|
||||
|
@@ -5,9 +5,10 @@ import type { CartEndpoint } from '.'
|
||||
import { formatCart } from '../../utils/cart'
|
||||
|
||||
const updateItem: CartEndpoint['handlers']['updateItem'] = async ({
|
||||
req,
|
||||
res,
|
||||
body: { cartId, itemId, item },
|
||||
config: { restFetch },
|
||||
config: { restBuyerFetch, tokenCookie },
|
||||
}) => {
|
||||
if (!cartId || !itemId || !item) {
|
||||
return res.status(400).json({
|
||||
@@ -16,34 +17,40 @@ const updateItem: CartEndpoint['handlers']['updateItem'] = async ({
|
||||
})
|
||||
}
|
||||
|
||||
// Get token from cookies
|
||||
const token = req.cookies[tokenCookie]
|
||||
|
||||
// Store specs
|
||||
let specs: RawVariant['Specs'] = []
|
||||
|
||||
// If a variant is present, fetch its specs
|
||||
if (item.variantId) {
|
||||
specs = await restFetch(
|
||||
specs = await restBuyerFetch(
|
||||
'GET',
|
||||
`/me/products/${item.productId}/variants/${item.variantId}`
|
||||
`/me/products/${item.productId}/variants/${item.variantId}`,
|
||||
null,
|
||||
{ token }
|
||||
).then((res: RawVariant) => res.Specs)
|
||||
}
|
||||
|
||||
// Add the item to the order
|
||||
await restFetch(
|
||||
await restBuyerFetch(
|
||||
'PATCH',
|
||||
`/orders/Outgoing/${cartId}/lineitems/${itemId}`,
|
||||
{
|
||||
ProductID: item.productId,
|
||||
Quantity: item.quantity,
|
||||
Specs: specs,
|
||||
}
|
||||
},
|
||||
{ token }
|
||||
)
|
||||
|
||||
// Get cart
|
||||
const [cart, lineItems] = await Promise.all([
|
||||
restFetch('GET', `/orders/Outgoing/${cartId}`),
|
||||
restFetch('GET', `/orders/Outgoing/${cartId}/lineitems`).then(
|
||||
(response: { Items: OrdercloudLineItem[] }) => response.Items
|
||||
),
|
||||
restBuyerFetch('GET', `/orders/Outgoing/${cartId}`, null, { token }),
|
||||
restBuyerFetch('GET', `/orders/Outgoing/${cartId}/lineitems`, null, {
|
||||
token,
|
||||
}).then((response: { Items: OrdercloudLineItem[] }) => response.Items),
|
||||
])
|
||||
|
||||
// Format cart
|
||||
|
@@ -3,7 +3,7 @@ import type { CheckoutEndpoint } from '.'
|
||||
const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({
|
||||
res,
|
||||
body: { cartId },
|
||||
config: { restFetch },
|
||||
config: { restBuyerFetch },
|
||||
}) => {
|
||||
// Return an error if no item is present
|
||||
if (!cartId) {
|
||||
@@ -14,12 +14,15 @@ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({
|
||||
}
|
||||
|
||||
// Register credit card
|
||||
const payments = await restFetch(
|
||||
const payments = await restBuyerFetch(
|
||||
'GET',
|
||||
`/orders/Outgoing/${cartId}/payments`
|
||||
).then((response: { Items: unknown[] }) => response.Items)
|
||||
|
||||
const address = await restFetch('GET', `/orders/Outgoing/${cartId}`).then(
|
||||
const address = await restBuyerFetch(
|
||||
'GET',
|
||||
`/orders/Outgoing/${cartId}`
|
||||
).then(
|
||||
(response: { ShippingAddressID: string }) => response.ShippingAddressID
|
||||
)
|
||||
|
||||
|
@@ -3,7 +3,7 @@ import type { CheckoutEndpoint } from '.'
|
||||
const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({
|
||||
res,
|
||||
body: { cartId },
|
||||
config: { restFetch },
|
||||
config: { restBuyerFetch },
|
||||
}) => {
|
||||
// Return an error if no item is present
|
||||
if (!cartId) {
|
||||
@@ -14,7 +14,7 @@ const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({
|
||||
}
|
||||
|
||||
// Submit order
|
||||
await restFetch('POST', `/orders/Outgoing/${cartId}/submit`, {})
|
||||
await restBuyerFetch('POST', `/orders/Outgoing/${cartId}/submit`, {})
|
||||
|
||||
// Return cart and errors
|
||||
res.status(200).json({ data: null, errors: [] })
|
||||
|
@@ -3,7 +3,7 @@ import type { CustomerAddressEndpoint } from '.'
|
||||
const addItem: CustomerAddressEndpoint['handlers']['addItem'] = async ({
|
||||
res,
|
||||
body: { item, cartId },
|
||||
config: { restFetch },
|
||||
config: { restBuyerFetch },
|
||||
}) => {
|
||||
// Return an error if no item is present
|
||||
if (!item) {
|
||||
@@ -22,25 +22,23 @@ const addItem: CustomerAddressEndpoint['handlers']['addItem'] = async ({
|
||||
}
|
||||
|
||||
// Register address
|
||||
const address = await restFetch('POST', `/me/addresses`, {
|
||||
"AddressName": "main address",
|
||||
"CompanyName": item.company,
|
||||
"FirstName": item.firstName,
|
||||
"LastName": item.lastName,
|
||||
"Street1": item.streetNumber,
|
||||
"Street2": item.streetNumber,
|
||||
"City": item.city,
|
||||
"State": item.city,
|
||||
"Zip": item.zipCode,
|
||||
"Country": item.country.slice(0, 2).toLowerCase(),
|
||||
"Shipping": true
|
||||
}).then(
|
||||
(response: {ID: string}) => response.ID
|
||||
)
|
||||
const address = await restBuyerFetch('POST', `/me/addresses`, {
|
||||
AddressName: 'main address',
|
||||
CompanyName: item.company,
|
||||
FirstName: item.firstName,
|
||||
LastName: item.lastName,
|
||||
Street1: item.streetNumber,
|
||||
Street2: item.streetNumber,
|
||||
City: item.city,
|
||||
State: item.city,
|
||||
Zip: item.zipCode,
|
||||
Country: item.country.slice(0, 2).toLowerCase(),
|
||||
Shipping: true,
|
||||
}).then((response: { ID: string }) => response.ID)
|
||||
|
||||
// Assign address to order
|
||||
await restFetch('PATCH', `/orders/Outgoing/${cartId}`, {
|
||||
ShippingAddressID: address
|
||||
await restBuyerFetch('PATCH', `/orders/Outgoing/${cartId}`, {
|
||||
ShippingAddressID: address,
|
||||
})
|
||||
|
||||
return res.status(200).json({ data: null, errors: [] })
|
||||
|
@@ -10,7 +10,7 @@ const stripe = new Stripe(process.env.STRIPE_SECRET as string, {
|
||||
const addItem: CustomerCardEndpoint['handlers']['addItem'] = async ({
|
||||
res,
|
||||
body: { item, cartId },
|
||||
config: { restFetch },
|
||||
config: { restBuyerFetch, restMiddlewareFetch },
|
||||
}) => {
|
||||
// Return an error if no item is present
|
||||
if (!item) {
|
||||
@@ -41,7 +41,7 @@ const addItem: CustomerCardEndpoint['handlers']['addItem'] = async ({
|
||||
.then((res: { id: string }) => res.id)
|
||||
|
||||
// Register credit card
|
||||
const creditCard = await restFetch('POST', `/me/creditcards`, {
|
||||
const creditCard = await restBuyerFetch('POST', `/me/creditcards`, {
|
||||
Token: token,
|
||||
CardType: 'credit',
|
||||
PartialAccountNumber: item.cardNumber.slice(-4),
|
||||
@@ -50,11 +50,23 @@ const addItem: CustomerCardEndpoint['handlers']['addItem'] = async ({
|
||||
}).then((response: OredercloudCreditCard) => response.ID)
|
||||
|
||||
// Assign payment to order
|
||||
await restFetch('POST', `/orders/Outgoing/${cartId}/payments`, {
|
||||
Accepted: true,
|
||||
Type: 'CreditCard',
|
||||
CreditCardID: creditCard,
|
||||
})
|
||||
const payment = await restBuyerFetch(
|
||||
'POST',
|
||||
`/orders/All/${cartId}/payments`,
|
||||
{
|
||||
Type: 'CreditCard',
|
||||
CreditCardID: creditCard,
|
||||
}
|
||||
).then((response: { ID: string }) => response.ID)
|
||||
|
||||
// Accept payment to order
|
||||
await restMiddlewareFetch(
|
||||
'PATCH',
|
||||
`/orders/All/${cartId}/payments/${payment}`,
|
||||
{
|
||||
Accepted: true,
|
||||
}
|
||||
)
|
||||
|
||||
return res.status(200).json({ data: null, errors: [] })
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { CommerceAPI, CommerceAPIConfig } from '@commerce/api'
|
||||
import { getCommerceApi as commerceApi } from '@commerce/api'
|
||||
import createRestFetcher from './utils/fetch-rest'
|
||||
import { createBuyerFetcher, createMiddlewareFetcher } from './utils/fetch-rest'
|
||||
import createGraphqlFetcher from './utils/fetch-graphql'
|
||||
|
||||
import getAllPages from './operations/get-all-pages'
|
||||
@@ -10,16 +10,29 @@ import getAllProductPaths from './operations/get-all-product-paths'
|
||||
import getAllProducts from './operations/get-all-products'
|
||||
import getProduct from './operations/get-product'
|
||||
|
||||
import { API_URL, API_VERSION, CART_COOKIE, CUSTOMER_COOKIE } from '../constants'
|
||||
import {
|
||||
API_URL,
|
||||
API_VERSION,
|
||||
CART_COOKIE,
|
||||
CUSTOMER_COOKIE,
|
||||
TOKEN_COOKIE,
|
||||
} from '../constants'
|
||||
|
||||
export interface OrdercloudConfig extends CommerceAPIConfig {
|
||||
restFetch: <T>(
|
||||
restBuyerFetch: <T>(
|
||||
method: string,
|
||||
resource: string,
|
||||
body?: Record<string, unknown>,
|
||||
fetchOptions?: Record<string, any>
|
||||
) => Promise<T>,
|
||||
apiVersion: string;
|
||||
) => Promise<T>
|
||||
restMiddlewareFetch: <T>(
|
||||
method: string,
|
||||
resource: string,
|
||||
body?: Record<string, unknown>,
|
||||
fetchOptions?: Record<string, any>
|
||||
) => Promise<T>
|
||||
apiVersion: string
|
||||
tokenCookie: string
|
||||
}
|
||||
|
||||
const config: OrdercloudConfig = {
|
||||
@@ -28,8 +41,12 @@ const config: OrdercloudConfig = {
|
||||
apiVersion: API_VERSION,
|
||||
cartCookie: CART_COOKIE,
|
||||
customerCookie: CUSTOMER_COOKIE,
|
||||
tokenCookie: TOKEN_COOKIE,
|
||||
cartCookieMaxAge: 2592000,
|
||||
restFetch: createRestFetcher(() => getCommerceApi().getConfig()),
|
||||
restBuyerFetch: createBuyerFetcher(() => getCommerceApi().getConfig()),
|
||||
restMiddlewareFetch: createMiddlewareFetcher(() =>
|
||||
getCommerceApi().getConfig()
|
||||
),
|
||||
fetch: createGraphqlFetcher(() => getCommerceApi().getConfig()),
|
||||
}
|
||||
|
||||
|
@@ -17,10 +17,10 @@ export default function getAllProductPathsOperation({
|
||||
config?: Partial<OrdercloudConfig>
|
||||
} = {}): Promise<T['data']> {
|
||||
// Get fetch from the config
|
||||
const { restFetch } = commerce.getConfig(config)
|
||||
const { restBuyerFetch } = commerce.getConfig(config)
|
||||
|
||||
// Get all products
|
||||
const rawProducts: RawProduct[] = await restFetch<{
|
||||
const rawProducts: RawProduct[] = await restBuyerFetch<{
|
||||
Items: RawProduct[]
|
||||
}>('GET', '/me/products').then((response) => response.Items)
|
||||
|
||||
|
@@ -18,10 +18,10 @@ export default function getAllProductsOperation({
|
||||
preview?: boolean
|
||||
} = {}): Promise<T['data']> {
|
||||
// Get fetch from the config
|
||||
const { restFetch } = commerce.getConfig(config)
|
||||
const { restBuyerFetch } = commerce.getConfig(config)
|
||||
|
||||
// Get all products
|
||||
const rawProducts: RawProduct[] = await restFetch<{
|
||||
const rawProducts: RawProduct[] = await restBuyerFetch<{
|
||||
Items: RawProduct[]
|
||||
}>('GET', '/me/products').then((response) => response.Items)
|
||||
|
||||
|
@@ -19,22 +19,22 @@ export default function getProductOperation({
|
||||
preview?: boolean
|
||||
} = {}): Promise<T['data']> {
|
||||
// Get fetch from the config
|
||||
const { restFetch } = commerce.getConfig(config)
|
||||
const { restBuyerFetch } = commerce.getConfig(config)
|
||||
|
||||
// Get a single product
|
||||
const productPromise = restFetch<RawProduct>(
|
||||
const productPromise = restBuyerFetch<RawProduct>(
|
||||
'GET',
|
||||
`/me/products/${variables?.slug}`
|
||||
)
|
||||
|
||||
// Get product specs
|
||||
const specsPromise = restFetch<{ Items: RawSpec[] }>(
|
||||
const specsPromise = restBuyerFetch<{ Items: RawSpec[] }>(
|
||||
'GET',
|
||||
`/me/products/${variables?.slug}/specs`
|
||||
).then((res) => res.Items)
|
||||
|
||||
// Get product variants
|
||||
const variantsPromise = restFetch<{ Items: RawVariant[] }>(
|
||||
const variantsPromise = restBuyerFetch<{ Items: RawVariant[] }>(
|
||||
'GET',
|
||||
`/me/products/${variables?.slug}/variants`
|
||||
).then((res) => res.Items)
|
||||
|
@@ -23,10 +23,10 @@ export default function getSiteInfoOperation({
|
||||
preview?: boolean
|
||||
} = {}): Promise<T['data']> {
|
||||
// Get fetch from the config
|
||||
const { restFetch } = commerce.getConfig(config)
|
||||
const { restBuyerFetch } = commerce.getConfig(config)
|
||||
|
||||
// Get list of categories
|
||||
const rawCategories: RawCategory[] = await restFetch<{
|
||||
const rawCategories: RawCategory[] = await restBuyerFetch<{
|
||||
Items: RawCategory[]
|
||||
}>('GET', `/me/categories`).then((response) => response.Items)
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import Cookies from 'js-cookie'
|
||||
import vercelFetch from '@vercel/fetch'
|
||||
import { FetcherError } from '@commerce/utils/errors'
|
||||
import jwt from 'jsonwebtoken'
|
||||
|
||||
import { OrdercloudConfig } from '../index'
|
||||
|
||||
@@ -8,7 +8,15 @@ import { OrdercloudConfig } from '../index'
|
||||
const fetch = vercelFetch()
|
||||
|
||||
// Get token util
|
||||
async function getToken(baseUrl: string) {
|
||||
async function getToken({
|
||||
baseUrl,
|
||||
clientId,
|
||||
clientSecret,
|
||||
}: {
|
||||
baseUrl: string
|
||||
clientId: string
|
||||
clientSecret?: string
|
||||
}): Promise<string> {
|
||||
// If not, get a new one and store it
|
||||
const authResponse = await fetch(`${baseUrl}/oauth/token`, {
|
||||
method: 'POST',
|
||||
@@ -16,7 +24,7 @@ async function getToken(baseUrl: string) {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
body: `client_id=${process.env.ORDERCLOUD_CLIENT_ID}&client_secret=${process.env.ORDERCLOUD_CLIENT_SECRET}&grant_type=client_credentials`,
|
||||
body: `client_id=${clientId}&client_secret=${clientSecret}&grant_type=client_credentials`,
|
||||
})
|
||||
|
||||
// If something failed getting the auth response
|
||||
@@ -32,68 +40,40 @@ async function getToken(baseUrl: string) {
|
||||
}
|
||||
|
||||
// Return the token
|
||||
return authResponse.json().then((response) => response.access_token)
|
||||
return authResponse
|
||||
.json()
|
||||
.then((response: { access_token: string }) => response.access_token)
|
||||
}
|
||||
|
||||
export async function fetchData<T>(
|
||||
opts: {
|
||||
path: string
|
||||
method: string
|
||||
baseUrl: string
|
||||
apiVersion: string
|
||||
fetchOptions?: Record<string, any>
|
||||
body?: Record<string, unknown>
|
||||
},
|
||||
retries = 0
|
||||
): Promise<T> {
|
||||
export async function fetchData<T>(opts: {
|
||||
token: string
|
||||
path: string
|
||||
method: string
|
||||
config: OrdercloudConfig
|
||||
fetchOptions?: Record<string, any>
|
||||
body?: Record<string, unknown>
|
||||
}): Promise<T> {
|
||||
// Destructure opts
|
||||
const { path, body, fetchOptions, baseUrl, apiVersion, method = 'GET' } = opts
|
||||
|
||||
// Decode token
|
||||
const decoded = jwt.decode(global.token as string) as jwt.JwtPayload | null
|
||||
|
||||
// If token is not present or its expired, get a new one and store it
|
||||
if (
|
||||
!global.token ||
|
||||
(typeof decoded?.exp === 'number' && decoded?.exp * 1000 < +new Date())
|
||||
) {
|
||||
// Get a new one
|
||||
const token = await getToken(baseUrl)
|
||||
|
||||
// Store it
|
||||
global.token = token
|
||||
}
|
||||
const { path, body, fetchOptions, config, token, method = 'GET' } = opts
|
||||
|
||||
// Do the request with the correct headers
|
||||
const dataResponse = await fetch(`${baseUrl}/${apiVersion}${path}`, {
|
||||
...fetchOptions,
|
||||
method,
|
||||
headers: {
|
||||
...fetchOptions?.headers,
|
||||
'Content-Type': 'application/json',
|
||||
accept: 'application/json, text/plain, */*',
|
||||
authorization: `Bearer ${global.token}`,
|
||||
},
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
})
|
||||
const dataResponse = await fetch(
|
||||
`${config.commerceUrl}/${config.apiVersion}${path}`,
|
||||
{
|
||||
...fetchOptions,
|
||||
method,
|
||||
headers: {
|
||||
...fetchOptions?.headers,
|
||||
'Content-Type': 'application/json',
|
||||
accept: 'application/json, text/plain, */*',
|
||||
authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
}
|
||||
)
|
||||
|
||||
// If something failed getting the data response
|
||||
if (!dataResponse.ok) {
|
||||
// If token is expired
|
||||
if (dataResponse.status === 401) {
|
||||
// Get a new one
|
||||
const token = await getToken(baseUrl)
|
||||
|
||||
// Store it
|
||||
global.token = token
|
||||
}
|
||||
|
||||
// And if retries left
|
||||
if (retries < 2) {
|
||||
// Refetch
|
||||
return fetchData(opts, retries + 1)
|
||||
}
|
||||
|
||||
// Get the body of it
|
||||
const error = await dataResponse.textConverted()
|
||||
|
||||
@@ -106,14 +86,20 @@ export async function fetchData<T>(
|
||||
|
||||
try {
|
||||
// Return data response as json
|
||||
return (await dataResponse.json()) as Promise<T>
|
||||
const data = (await dataResponse.json()) as Promise<T>
|
||||
|
||||
// Return data with meta
|
||||
return {
|
||||
meta: { token },
|
||||
...data,
|
||||
}
|
||||
} catch (error) {
|
||||
// If response is empty return it as text
|
||||
return null as unknown as Promise<T>
|
||||
}
|
||||
}
|
||||
|
||||
const serverFetcher: (
|
||||
export const createMiddlewareFetcher: (
|
||||
getConfig: () => OrdercloudConfig
|
||||
) => <T>(
|
||||
method: string,
|
||||
@@ -129,17 +115,64 @@ const serverFetcher: (
|
||||
fetchOptions?: Record<string, any>
|
||||
) => {
|
||||
// Get provider config
|
||||
const { commerceUrl, apiVersion } = getConfig()
|
||||
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<T>({
|
||||
token,
|
||||
fetchOptions,
|
||||
method,
|
||||
baseUrl: commerceUrl,
|
||||
apiVersion,
|
||||
config,
|
||||
path,
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
export default serverFetcher
|
||||
export const createBuyerFetcher: (
|
||||
getConfig: () => OrdercloudConfig
|
||||
) => <T>(
|
||||
method: string,
|
||||
path: string,
|
||||
body?: Record<string, unknown>,
|
||||
fetchOptions?: Record<string, any>
|
||||
) => Promise<T> =
|
||||
(getConfig) =>
|
||||
async <T>(
|
||||
method: string,
|
||||
path: string,
|
||||
body?: Record<string, unknown>,
|
||||
fetchOptions?: Record<string, any>
|
||||
) => {
|
||||
// Get provider config
|
||||
const config = getConfig()
|
||||
|
||||
// If a token was passed, set it on global
|
||||
if (fetchOptions?.token) {
|
||||
global.token = fetchOptions.token
|
||||
}
|
||||
|
||||
// Get a token
|
||||
if (!global.token) {
|
||||
global.token = await getToken({
|
||||
baseUrl: config.commerceUrl,
|
||||
clientId: process.env.ORDERCLOUD_BUYER_CLIENT_ID as string,
|
||||
})
|
||||
}
|
||||
|
||||
// Return the data and specify the expected type
|
||||
return fetchData<T>({
|
||||
token: global.token as string,
|
||||
fetchOptions,
|
||||
config,
|
||||
method,
|
||||
path,
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
export const CART_COOKIE = 'ordercloud.cart'
|
||||
export const TOKEN_COOKIE = 'ordercloud.token'
|
||||
export const CUSTOMER_COOKIE = 'ordercloud.customer'
|
||||
export const API_URL = 'https://sandboxapi.ordercloud.io'
|
||||
export const API_VERSION = 'v1'
|
||||
|
Reference in New Issue
Block a user