Save token to cookie

This commit is contained in:
goncy
2021-09-30 14:56:04 -03:00
parent efa1521bb9
commit 4ab3cf5214
15 changed files with 274 additions and 161 deletions

View File

@@ -9,7 +9,7 @@ import { formatCart } from '../../utils/cart'
const addItem: CartEndpoint['handlers']['addItem'] = async ({ const addItem: CartEndpoint['handlers']['addItem'] = async ({
res, res,
body: { cartId, item }, body: { cartId, item },
config: { restFetch, cartCookie }, config: { restBuyerFetch, cartCookie, tokenCookie },
}) => { }) => {
// Return an error if no item is present // Return an error if no item is present
if (!item) { if (!item) {
@@ -19,52 +19,74 @@ const addItem: CartEndpoint['handlers']['addItem'] = async ({
}) })
} }
// Store token
let token
// Set the quantity if not present // Set the quantity if not present
if (!item.quantity) item.quantity = 1 if (!item.quantity) item.quantity = 1
// Create an order if it doesn't exist // Create an order if it doesn't exist
if (!cartId) { if (!cartId) {
cartId = await restFetch('POST', `/orders/Outgoing`, {}).then( const { ID, meta } = await restBuyerFetch(
(response: { ID: string }) => response.ID 'POST',
) `/orders/Outgoing`,
} {}
).then((response: { ID: string; meta: { token: string } }) => response)
// Set the cart cookie // Set the cart id and token
res.setHeader( cartId = ID
'Set-Cookie', token = meta.token
serialize(cartCookie, cartId, {
maxAge: 60 * 60 * 24 * 30, // Set the cart and token cookie
expires: new Date(Date.now() + 60 * 60 * 24 * 30 * 1000), res.setHeader('Set-Cookie', [
secure: process.env.NODE_ENV === 'production', serialize(tokenCookie, meta.token, {
path: '/', maxAge: 60 * 60 * 24 * 30,
sameSite: 'lax', 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 // Store specs
let specs: RawVariant['Specs'] = [] let specs: RawVariant['Specs'] = []
// If a variant is present, fetch its specs // If a variant is present, fetch its specs
if (item.variantId) { if (item.variantId) {
specs = await restFetch( specs = await restBuyerFetch(
'GET', 'GET',
`/me/products/${item.productId}/variants/${item.variantId}` `/me/products/${item.productId}/variants/${item.variantId}`,
null,
{ token }
).then((res: RawVariant) => res.Specs) ).then((res: RawVariant) => res.Specs)
} }
// Add the item to the order // Add the item to the order
await restFetch('POST', `/orders/Outgoing/${cartId}/lineitems`, { await restBuyerFetch(
ProductID: item.productId, 'POST',
Quantity: item.quantity, `/orders/Outgoing/${cartId}/lineitems`,
Specs: specs, {
}) ProductID: item.productId,
Quantity: item.quantity,
Specs: specs,
},
{ token }
)
// Get cart // Get cart
const [cart, lineItems] = await Promise.all([ const [cart, lineItems] = await Promise.all([
restFetch('GET', `/orders/Outgoing/${cartId}`), restBuyerFetch('GET', `/orders/Outgoing/${cartId}`, null, { token }),
restFetch('GET', `/orders/Outgoing/${cartId}/lineitems`).then( restBuyerFetch('GET', `/orders/Outgoing/${cartId}/lineitems`, null, {
(response: { Items: OrdercloudLineItem[] }) => response.Items token,
), }).then((response: { Items: OrdercloudLineItem[] }) => response.Items),
]) ])
// Format cart // Format cart

View File

@@ -7,9 +7,10 @@ import { formatCart } from '../../utils/cart'
// Return current cart info // Return current cart info
const getCart: CartEndpoint['handlers']['getCart'] = async ({ const getCart: CartEndpoint['handlers']['getCart'] = async ({
req,
res, res,
body: { cartId }, body: { cartId },
config: { restFetch, cartCookie }, config: { restBuyerFetch, cartCookie, tokenCookie },
}) => { }) => {
if (!cartId) { if (!cartId) {
return res.status(400).json({ return res.status(400).json({
@@ -19,13 +20,23 @@ const getCart: CartEndpoint['handlers']['getCart'] = async ({
} }
try { try {
// Get token from cookies
const token = req.cookies[tokenCookie]
// Get cart // Get cart
const cart = await restFetch('GET', `/orders/Outgoing/${cartId}`) const cart = await restBuyerFetch(
'GET',
`/orders/Outgoing/${cartId}`,
null,
{ token }
)
// Get line items // Get line items
const lineItems = await restFetch( const lineItems = await restBuyerFetch(
'GET', 'GET',
`/orders/Outgoing/${cartId}/lineitems` `/orders/Outgoing/${cartId}/lineitems`,
null,
{ token }
).then((response: { Items: OrdercloudLineItem[] }) => response.Items) ).then((response: { Items: OrdercloudLineItem[] }) => response.Items)
// Format cart // Format cart
@@ -34,14 +45,17 @@ const getCart: CartEndpoint['handlers']['getCart'] = async ({
// Return cart and errors // Return cart and errors
res.status(200).json({ data: formattedCart, errors: [] }) res.status(200).json({ data: formattedCart, errors: [] })
} catch (error) { } catch (error) {
// Reset cart cookie // Reset cart and token cookie
res.setHeader( res.setHeader('Set-Cookie', [
'Set-Cookie',
serialize(cartCookie, cartId, { serialize(cartCookie, cartId, {
maxAge: -1, maxAge: -1,
path: '/', path: '/',
}) }),
) serialize(tokenCookie, cartId, {
maxAge: -1,
path: '/',
}),
])
// Return empty cart // Return empty cart
res.status(200).json({ data: null, errors: [] }) res.status(200).json({ data: null, errors: [] })

View File

@@ -4,9 +4,10 @@ import { formatCart } from '../../utils/cart'
import { OrdercloudLineItem } from '../../../types/cart' import { OrdercloudLineItem } from '../../../types/cart'
const removeItem: CartEndpoint['handlers']['removeItem'] = async ({ const removeItem: CartEndpoint['handlers']['removeItem'] = async ({
req,
res, res,
body: { cartId, itemId }, body: { cartId, itemId },
config: { restFetch }, config: { restBuyerFetch, tokenCookie },
}) => { }) => {
if (!cartId || !itemId) { if (!cartId || !itemId) {
return res.status(400).json({ 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 // Remove the item to the order
await restFetch( await restBuyerFetch(
'DELETE', 'DELETE',
`/orders/Outgoing/${cartId}/lineitems/${itemId}` `/orders/Outgoing/${cartId}/lineitems/${itemId}`,
null,
{ token }
) )
// Get cart // Get cart
const [cart, lineItems] = await Promise.all([ const [cart, lineItems] = await Promise.all([
restFetch('GET', `/orders/Outgoing/${cartId}`), restBuyerFetch('GET', `/orders/Outgoing/${cartId}`, null, { token }),
restFetch('GET', `/orders/Outgoing/${cartId}/lineitems`).then( restBuyerFetch('GET', `/orders/Outgoing/${cartId}/lineitems`, null, {
(response: { Items: OrdercloudLineItem[] }) => response.Items token,
), }).then((response: { Items: OrdercloudLineItem[] }) => response.Items),
]) ])
// Format cart // Format cart

View File

@@ -5,9 +5,10 @@ import type { CartEndpoint } from '.'
import { formatCart } from '../../utils/cart' import { formatCart } from '../../utils/cart'
const updateItem: CartEndpoint['handlers']['updateItem'] = async ({ const updateItem: CartEndpoint['handlers']['updateItem'] = async ({
req,
res, res,
body: { cartId, itemId, item }, body: { cartId, itemId, item },
config: { restFetch }, config: { restBuyerFetch, tokenCookie },
}) => { }) => {
if (!cartId || !itemId || !item) { if (!cartId || !itemId || !item) {
return res.status(400).json({ 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 // Store specs
let specs: RawVariant['Specs'] = [] let specs: RawVariant['Specs'] = []
// If a variant is present, fetch its specs // If a variant is present, fetch its specs
if (item.variantId) { if (item.variantId) {
specs = await restFetch( specs = await restBuyerFetch(
'GET', 'GET',
`/me/products/${item.productId}/variants/${item.variantId}` `/me/products/${item.productId}/variants/${item.variantId}`,
null,
{ token }
).then((res: RawVariant) => res.Specs) ).then((res: RawVariant) => res.Specs)
} }
// Add the item to the order // Add the item to the order
await restFetch( await restBuyerFetch(
'PATCH', 'PATCH',
`/orders/Outgoing/${cartId}/lineitems/${itemId}`, `/orders/Outgoing/${cartId}/lineitems/${itemId}`,
{ {
ProductID: item.productId, ProductID: item.productId,
Quantity: item.quantity, Quantity: item.quantity,
Specs: specs, Specs: specs,
} },
{ token }
) )
// Get cart // Get cart
const [cart, lineItems] = await Promise.all([ const [cart, lineItems] = await Promise.all([
restFetch('GET', `/orders/Outgoing/${cartId}`), restBuyerFetch('GET', `/orders/Outgoing/${cartId}`, null, { token }),
restFetch('GET', `/orders/Outgoing/${cartId}/lineitems`).then( restBuyerFetch('GET', `/orders/Outgoing/${cartId}/lineitems`, null, {
(response: { Items: OrdercloudLineItem[] }) => response.Items token,
), }).then((response: { Items: OrdercloudLineItem[] }) => response.Items),
]) ])
// Format cart // Format cart

View File

@@ -3,7 +3,7 @@ import type { CheckoutEndpoint } from '.'
const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({
res, res,
body: { cartId }, body: { cartId },
config: { restFetch }, config: { restBuyerFetch },
}) => { }) => {
// Return an error if no item is present // Return an error if no item is present
if (!cartId) { if (!cartId) {
@@ -14,12 +14,15 @@ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({
} }
// Register credit card // Register credit card
const payments = await restFetch( const payments = await restBuyerFetch(
'GET', 'GET',
`/orders/Outgoing/${cartId}/payments` `/orders/Outgoing/${cartId}/payments`
).then((response: { Items: unknown[] }) => response.Items) ).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 (response: { ShippingAddressID: string }) => response.ShippingAddressID
) )

View File

@@ -3,7 +3,7 @@ import type { CheckoutEndpoint } from '.'
const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({ const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({
res, res,
body: { cartId }, body: { cartId },
config: { restFetch }, config: { restBuyerFetch },
}) => { }) => {
// Return an error if no item is present // Return an error if no item is present
if (!cartId) { if (!cartId) {
@@ -14,7 +14,7 @@ const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({
} }
// Submit order // Submit order
await restFetch('POST', `/orders/Outgoing/${cartId}/submit`, {}) await restBuyerFetch('POST', `/orders/Outgoing/${cartId}/submit`, {})
// Return cart and errors // Return cart and errors
res.status(200).json({ data: null, errors: [] }) res.status(200).json({ data: null, errors: [] })

View File

@@ -3,7 +3,7 @@ import type { CustomerAddressEndpoint } from '.'
const addItem: CustomerAddressEndpoint['handlers']['addItem'] = async ({ const addItem: CustomerAddressEndpoint['handlers']['addItem'] = async ({
res, res,
body: { item, cartId }, body: { item, cartId },
config: { restFetch }, config: { restBuyerFetch },
}) => { }) => {
// Return an error if no item is present // Return an error if no item is present
if (!item) { if (!item) {
@@ -22,25 +22,23 @@ const addItem: CustomerAddressEndpoint['handlers']['addItem'] = async ({
} }
// Register address // Register address
const address = await restFetch('POST', `/me/addresses`, { const address = await restBuyerFetch('POST', `/me/addresses`, {
"AddressName": "main address", AddressName: 'main address',
"CompanyName": item.company, CompanyName: item.company,
"FirstName": item.firstName, FirstName: item.firstName,
"LastName": item.lastName, LastName: item.lastName,
"Street1": item.streetNumber, Street1: item.streetNumber,
"Street2": item.streetNumber, Street2: item.streetNumber,
"City": item.city, City: item.city,
"State": item.city, State: item.city,
"Zip": item.zipCode, Zip: item.zipCode,
"Country": item.country.slice(0, 2).toLowerCase(), Country: item.country.slice(0, 2).toLowerCase(),
"Shipping": true Shipping: true,
}).then( }).then((response: { ID: string }) => response.ID)
(response: {ID: string}) => response.ID
)
// Assign address to order // Assign address to order
await restFetch('PATCH', `/orders/Outgoing/${cartId}`, { await restBuyerFetch('PATCH', `/orders/Outgoing/${cartId}`, {
ShippingAddressID: address ShippingAddressID: address,
}) })
return res.status(200).json({ data: null, errors: [] }) return res.status(200).json({ data: null, errors: [] })

View File

@@ -10,7 +10,7 @@ const stripe = new Stripe(process.env.STRIPE_SECRET as string, {
const addItem: CustomerCardEndpoint['handlers']['addItem'] = async ({ const addItem: CustomerCardEndpoint['handlers']['addItem'] = async ({
res, res,
body: { item, cartId }, body: { item, cartId },
config: { restFetch }, config: { restBuyerFetch, restMiddlewareFetch },
}) => { }) => {
// Return an error if no item is present // Return an error if no item is present
if (!item) { if (!item) {
@@ -41,7 +41,7 @@ const addItem: CustomerCardEndpoint['handlers']['addItem'] = async ({
.then((res: { id: string }) => res.id) .then((res: { id: string }) => res.id)
// Register credit card // Register credit card
const creditCard = await restFetch('POST', `/me/creditcards`, { const creditCard = await restBuyerFetch('POST', `/me/creditcards`, {
Token: token, Token: token,
CardType: 'credit', CardType: 'credit',
PartialAccountNumber: item.cardNumber.slice(-4), PartialAccountNumber: item.cardNumber.slice(-4),
@@ -50,11 +50,23 @@ const addItem: CustomerCardEndpoint['handlers']['addItem'] = async ({
}).then((response: OredercloudCreditCard) => response.ID) }).then((response: OredercloudCreditCard) => response.ID)
// Assign payment to order // Assign payment to order
await restFetch('POST', `/orders/Outgoing/${cartId}/payments`, { const payment = await restBuyerFetch(
Accepted: true, 'POST',
Type: 'CreditCard', `/orders/All/${cartId}/payments`,
CreditCardID: creditCard, {
}) 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: [] }) return res.status(200).json({ data: null, errors: [] })
} }

View File

@@ -1,6 +1,6 @@
import type { CommerceAPI, CommerceAPIConfig } from '@commerce/api' import type { CommerceAPI, CommerceAPIConfig } from '@commerce/api'
import { getCommerceApi as commerceApi } 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 createGraphqlFetcher from './utils/fetch-graphql'
import getAllPages from './operations/get-all-pages' 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 getAllProducts from './operations/get-all-products'
import getProduct from './operations/get-product' 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 { export interface OrdercloudConfig extends CommerceAPIConfig {
restFetch: <T>( restBuyerFetch: <T>(
method: string, method: string,
resource: string, resource: string,
body?: Record<string, unknown>, body?: Record<string, unknown>,
fetchOptions?: Record<string, any> fetchOptions?: Record<string, any>
) => Promise<T>, ) => Promise<T>
apiVersion: string; restMiddlewareFetch: <T>(
method: string,
resource: string,
body?: Record<string, unknown>,
fetchOptions?: Record<string, any>
) => Promise<T>
apiVersion: string
tokenCookie: string
} }
const config: OrdercloudConfig = { const config: OrdercloudConfig = {
@@ -28,8 +41,12 @@ const config: OrdercloudConfig = {
apiVersion: API_VERSION, apiVersion: API_VERSION,
cartCookie: CART_COOKIE, cartCookie: CART_COOKIE,
customerCookie: CUSTOMER_COOKIE, customerCookie: CUSTOMER_COOKIE,
tokenCookie: TOKEN_COOKIE,
cartCookieMaxAge: 2592000, cartCookieMaxAge: 2592000,
restFetch: createRestFetcher(() => getCommerceApi().getConfig()), restBuyerFetch: createBuyerFetcher(() => getCommerceApi().getConfig()),
restMiddlewareFetch: createMiddlewareFetcher(() =>
getCommerceApi().getConfig()
),
fetch: createGraphqlFetcher(() => getCommerceApi().getConfig()), fetch: createGraphqlFetcher(() => getCommerceApi().getConfig()),
} }

View File

@@ -17,10 +17,10 @@ export default function getAllProductPathsOperation({
config?: Partial<OrdercloudConfig> config?: Partial<OrdercloudConfig>
} = {}): Promise<T['data']> { } = {}): Promise<T['data']> {
// Get fetch from the config // Get fetch from the config
const { restFetch } = commerce.getConfig(config) const { restBuyerFetch } = commerce.getConfig(config)
// Get all products // Get all products
const rawProducts: RawProduct[] = await restFetch<{ const rawProducts: RawProduct[] = await restBuyerFetch<{
Items: RawProduct[] Items: RawProduct[]
}>('GET', '/me/products').then((response) => response.Items) }>('GET', '/me/products').then((response) => response.Items)

View File

@@ -18,10 +18,10 @@ export default function getAllProductsOperation({
preview?: boolean preview?: boolean
} = {}): Promise<T['data']> { } = {}): Promise<T['data']> {
// Get fetch from the config // Get fetch from the config
const { restFetch } = commerce.getConfig(config) const { restBuyerFetch } = commerce.getConfig(config)
// Get all products // Get all products
const rawProducts: RawProduct[] = await restFetch<{ const rawProducts: RawProduct[] = await restBuyerFetch<{
Items: RawProduct[] Items: RawProduct[]
}>('GET', '/me/products').then((response) => response.Items) }>('GET', '/me/products').then((response) => response.Items)

View File

@@ -19,22 +19,22 @@ export default function getProductOperation({
preview?: boolean preview?: boolean
} = {}): Promise<T['data']> { } = {}): Promise<T['data']> {
// Get fetch from the config // Get fetch from the config
const { restFetch } = commerce.getConfig(config) const { restBuyerFetch } = commerce.getConfig(config)
// Get a single product // Get a single product
const productPromise = restFetch<RawProduct>( const productPromise = restBuyerFetch<RawProduct>(
'GET', 'GET',
`/me/products/${variables?.slug}` `/me/products/${variables?.slug}`
) )
// Get product specs // Get product specs
const specsPromise = restFetch<{ Items: RawSpec[] }>( const specsPromise = restBuyerFetch<{ Items: RawSpec[] }>(
'GET', 'GET',
`/me/products/${variables?.slug}/specs` `/me/products/${variables?.slug}/specs`
).then((res) => res.Items) ).then((res) => res.Items)
// Get product variants // Get product variants
const variantsPromise = restFetch<{ Items: RawVariant[] }>( const variantsPromise = restBuyerFetch<{ Items: RawVariant[] }>(
'GET', 'GET',
`/me/products/${variables?.slug}/variants` `/me/products/${variables?.slug}/variants`
).then((res) => res.Items) ).then((res) => res.Items)

View File

@@ -23,10 +23,10 @@ export default function getSiteInfoOperation({
preview?: boolean preview?: boolean
} = {}): Promise<T['data']> { } = {}): Promise<T['data']> {
// Get fetch from the config // Get fetch from the config
const { restFetch } = commerce.getConfig(config) const { restBuyerFetch } = commerce.getConfig(config)
// Get list of categories // Get list of categories
const rawCategories: RawCategory[] = await restFetch<{ const rawCategories: RawCategory[] = await restBuyerFetch<{
Items: RawCategory[] Items: RawCategory[]
}>('GET', `/me/categories`).then((response) => response.Items) }>('GET', `/me/categories`).then((response) => response.Items)

View File

@@ -1,6 +1,6 @@
import Cookies from 'js-cookie'
import vercelFetch from '@vercel/fetch' import vercelFetch from '@vercel/fetch'
import { FetcherError } from '@commerce/utils/errors' import { FetcherError } from '@commerce/utils/errors'
import jwt from 'jsonwebtoken'
import { OrdercloudConfig } from '../index' import { OrdercloudConfig } from '../index'
@@ -8,7 +8,15 @@ import { OrdercloudConfig } from '../index'
const fetch = vercelFetch() const fetch = vercelFetch()
// Get token util // 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 // If not, get a new one and store it
const authResponse = await fetch(`${baseUrl}/oauth/token`, { const authResponse = await fetch(`${baseUrl}/oauth/token`, {
method: 'POST', method: 'POST',
@@ -16,7 +24,7 @@ async function getToken(baseUrl: string) {
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json', 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 // If something failed getting the auth response
@@ -32,68 +40,40 @@ async function getToken(baseUrl: string) {
} }
// Return the token // 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>( export async function fetchData<T>(opts: {
opts: { token: string
path: string path: string
method: string method: string
baseUrl: string config: OrdercloudConfig
apiVersion: string fetchOptions?: Record<string, any>
fetchOptions?: Record<string, any> body?: Record<string, unknown>
body?: Record<string, unknown> }): Promise<T> {
},
retries = 0
): Promise<T> {
// Destructure opts // Destructure opts
const { path, body, fetchOptions, baseUrl, apiVersion, method = 'GET' } = opts const { path, body, fetchOptions, config, token, 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
}
// Do the request with the correct headers // Do the request with the correct headers
const dataResponse = await fetch(`${baseUrl}/${apiVersion}${path}`, { const dataResponse = await fetch(
...fetchOptions, `${config.commerceUrl}/${config.apiVersion}${path}`,
method, {
headers: { ...fetchOptions,
...fetchOptions?.headers, method,
'Content-Type': 'application/json', headers: {
accept: 'application/json, text/plain, */*', ...fetchOptions?.headers,
authorization: `Bearer ${global.token}`, 'Content-Type': 'application/json',
}, accept: 'application/json, text/plain, */*',
body: body ? JSON.stringify(body) : undefined, authorization: `Bearer ${token}`,
}) },
body: body ? JSON.stringify(body) : undefined,
}
)
// If something failed getting the data response // If something failed getting the data response
if (!dataResponse.ok) { 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 // Get the body of it
const error = await dataResponse.textConverted() const error = await dataResponse.textConverted()
@@ -106,14 +86,20 @@ export async function fetchData<T>(
try { try {
// Return data response as json // 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) { } catch (error) {
// If response is empty return it as text // If response is empty return it as text
return null as unknown as Promise<T> return null as unknown as Promise<T>
} }
} }
const serverFetcher: ( export const createMiddlewareFetcher: (
getConfig: () => OrdercloudConfig getConfig: () => OrdercloudConfig
) => <T>( ) => <T>(
method: string, method: string,
@@ -129,17 +115,64 @@ const serverFetcher: (
fetchOptions?: Record<string, any> fetchOptions?: Record<string, any>
) => { ) => {
// Get provider config // 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 the data and specify the expected type
return fetchData<T>({ return fetchData<T>({
token,
fetchOptions, fetchOptions,
method, method,
baseUrl: commerceUrl, config,
apiVersion,
path, path,
body, 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,
})
}

View File

@@ -1,4 +1,5 @@
export const CART_COOKIE = 'ordercloud.cart' export const CART_COOKIE = 'ordercloud.cart'
export const TOKEN_COOKIE = 'ordercloud.token'
export const CUSTOMER_COOKIE = 'ordercloud.customer' export const CUSTOMER_COOKIE = 'ordercloud.customer'
export const API_URL = 'https://sandboxapi.ordercloud.io' export const API_URL = 'https://sandboxapi.ordercloud.io'
export const API_VERSION = 'v1' export const API_VERSION = 'v1'