Skip to content

Commit

Permalink
Fix auth & wishlist (#918)
Browse files Browse the repository at this point in the history
* Fix auth & wishlist

* Revert files

* Update signup.ts

* Update signup.ts

* Requested changes

* Revert fetch options
  • Loading branch information
cond0r authored Jan 30, 2023
1 parent 2523557 commit d1d9e8c
Show file tree
Hide file tree
Showing 39 changed files with 203 additions and 146 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ const getLoggedInCustomer: CustomerEndpoint['handlers']['getLoggedInCustomer'] =
getLoggedInCustomerQuery,
undefined,
{
'Set-Cookie': `${config.customerCookie}=${token}`,
headers: {
cookie: `${config.customerCookie}=${token}`,
},
}
)
const { customer } = data
Expand Down
14 changes: 7 additions & 7 deletions packages/bigcommerce/src/api/endpoints/login/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ const login: LoginEndpoint['handlers']['login'] = async ({
commerce,
}) => {
try {
const res = new Response()
await commerce.login({ variables: { email, password }, config, res })
return {
status: res.status,
headers: res.headers,
}
const response = await commerce.login({
variables: { email, password },
config,
})

return response
} catch (error) {
// Check if the email and password didn't match an existing account
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 }
{ status: 401 }
)
} else {
throw error
Expand Down
32 changes: 15 additions & 17 deletions packages/bigcommerce/src/api/endpoints/signup/signup.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { SignupEndpoint } from '.'
import { CommerceAPIError } from '@vercel/commerce/api/utils/errors'

import { BigcommerceApiError } from '../../utils/errors'

const signup: SignupEndpoint['handlers']['signup'] = async ({
Expand All @@ -22,28 +21,27 @@ const signup: SignupEndpoint['handlers']['signup'] = async ({
},
]),
})

// Login the customer right after creating it
const response = await commerce.login({
variables: { email, password },
config,
})

return response
} 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) {
throw new CommerceAPIError('Email already in use', {
// Display all validation errors from BigCommerce in a single error message
if (error instanceof BigcommerceApiError && error.status >= 400) {
const message = Object.values(error.data.errors).join('<br />')
if (message) {
throw new CommerceAPIError(message, {
status: 400,
code: 'duplicated_email',
code: 'invalid_request',
})
}
} else {
throw error
}
}

const res = new Response()

// Login the customer right after creating it
await commerce.login({ variables: { email, password }, res, config })

return {
headers: res.headers,
throw error
}
}

Expand Down
7 changes: 5 additions & 2 deletions packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { parseWishlistItem } from '../../utils/parse-item'
import getCustomerId from '../../utils/get-customer-id'
import type { WishlistEndpoint } from '.'
import { normalizeWishlist } from '../../../lib/normalize'

const addItem: WishlistEndpoint['handlers']['addItem'] = async ({
body: { customerToken, item },
Expand Down Expand Up @@ -31,7 +32,7 @@ const addItem: WishlistEndpoint['handlers']['addItem'] = async ({
}),
})
return {
data,
data: normalizeWishlist(data),
}
}

Expand All @@ -47,7 +48,9 @@ const addItem: WishlistEndpoint['handlers']['addItem'] = async ({
)

// Returns Wishlist
return { data }
return {
data: normalizeWishlist(data),
}
}

export default addItem
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
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'

Expand All @@ -9,8 +8,6 @@ const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({
config,
commerce,
}) => {
let result: { data?: Wishlist } = {}

if (customerToken) {
const customerId =
customerToken && (await getCustomerId({ customerToken, config }))
Expand All @@ -25,10 +22,10 @@ const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({
config,
})

result = { data: wishlist }
return { data: wishlist }
}

return { data: result.data ?? null }
return { data: null }
}

export default getWishlist
12 changes: 7 additions & 5 deletions packages/bigcommerce/src/api/endpoints/wishlist/remove-item.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { Wishlist } from '@vercel/commerce/types/wishlist'
import getCustomerId from '../../utils/get-customer-id'
import type { WishlistEndpoint } from '.'
import type { BCWishlist } from '../../utils/types'

import getCustomerId from '../../utils/get-customer-id'
import { CommerceAPIError } from '@vercel/commerce/api/utils/errors'
import { normalizeWishlist } from '../../../lib/normalize'

// Return wishlist info
const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({
Expand All @@ -11,6 +13,7 @@ const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({
}) => {
const customerId =
customerToken && (await getCustomerId({ customerToken, config }))

const { wishlist } =
(customerId &&
(await commerce.getCustomerWishlist({
Expand All @@ -23,13 +26,12 @@ const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({
throw new CommerceAPIError('Wishlist not found', { status: 400 })
}

const result = await config.storeApiFetch<{ data: Wishlist } | null>(
const result = await config.storeApiFetch<{ data: BCWishlist } | null>(
`/v3/wishlists/${wishlist.id}/items/${itemId}`,
{ method: 'DELETE' }
)
const data = result?.data ?? null

return { data }
return { data: result?.data ? normalizeWishlist(result.data) : null }
}

export default removeItem
32 changes: 17 additions & 15 deletions packages/bigcommerce/src/api/operations/get-customer-wishlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ import type {
OperationContext,
OperationOptions,
} from '@vercel/commerce/api/operations'
import type {
GetCustomerWishlistOperation,
Wishlist,
} from '@vercel/commerce/types/wishlist'
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
import type { GetCustomerWishlistOperation } from '@vercel/commerce/types/wishlist'
import type { RecursivePartial, BCWishlist } from '../utils/types'
import { BigcommerceConfig, Provider } from '..'
import getAllProducts, { ProductEdge } from './get-all-products'
import { ProductEdge } from './get-all-products'
import { normalizeWishlist } from '../../lib/normalize'

export default function getCustomerWishlistOperation({
commerce,
Expand Down Expand Up @@ -41,18 +39,22 @@ export default function getCustomerWishlistOperation({
}): Promise<T['data']> {
config = commerce.getConfig(config)

const { data = [] } = await config.storeApiFetch<
RecursivePartial<{ data: Wishlist[] }>
>(`/v3/wishlists?customer_id=${variables.customerId}`)
const { data = [] } = await config.storeApiFetch<{ data: BCWishlist[] }>(
`/v3/wishlists?customer_id=${variables.customerId}`
)

const wishlist = data[0]

if (includeProducts && wishlist?.items?.length) {
const ids = wishlist.items
?.map((item) => (item?.productId ? String(item?.productId) : null))
.filter((id): id is string => !!id)
const ids = []

for (const wishlistItem of wishlist.items) {
if (wishlistItem.product_id) {
ids.push(String(wishlistItem.product_id))
}
}

if (ids?.length) {
if (ids.length) {
const graphqlData = await commerce.getAllProducts({
variables: { first: 50, ids },
config,
Expand All @@ -66,7 +68,7 @@ export default function getCustomerWishlistOperation({
}, {})
// Populate the wishlist items with the graphql products
wishlist.items.forEach((item) => {
const product = item && productsById[Number(item.productId)]
const product = item && productsById[Number(item.product_id)]
if (item && product) {
// @ts-ignore Fix this type when the wishlist type is properly defined
item.product = product
Expand All @@ -75,7 +77,7 @@ export default function getCustomerWishlistOperation({
}
}

return { wishlist: wishlist as RecursiveRequired<typeof wishlist> }
return { wishlist: wishlist && normalizeWishlist(wishlist) }
}

return getCustomerWishlist
Expand Down
21 changes: 7 additions & 14 deletions packages/bigcommerce/src/api/operations/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type {
import type { LoginOperation } from '@vercel/commerce/types/login'
import type { LoginMutation } from '../../../schema'
import type { RecursivePartial } from '../utils/types'
import concatHeader from '../utils/concat-cookie'
import type { BigcommerceConfig, Provider } from '..'

export const loginMutation = /* GraphQL */ `
Expand All @@ -22,26 +21,23 @@ export default function loginOperation({
async function login<T extends LoginOperation>(opts: {
variables: T['variables']
config?: BigcommerceConfig
res: Response
}): Promise<T['data']>

async function login<T extends LoginOperation>(
opts: {
variables: T['variables']
config?: BigcommerceConfig
res: Response
} & OperationOptions
): Promise<T['data']>

async function login<T extends LoginOperation>({
query = loginMutation,
variables,
res: response,
config,
}: {
query?: string
variables: T['variables']
res: Response

config?: BigcommerceConfig
}): Promise<T['data']> {
config = commerce.getConfig(config)
Expand All @@ -50,6 +46,9 @@ export default function loginOperation({
query,
{ variables }
)

const headers = new Headers()

// Bigcommerce returns a Set-Cookie header with the auth cookie
let cookie = res.headers.get('Set-Cookie')

Expand All @@ -63,19 +62,13 @@ export default function loginOperation({
cookie = cookie.replace(/; SameSite=none/gi, '; SameSite=lax')
}

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)
)
}
headers.set('Set-Cookie', cookie)
}

return {
result: data.login?.result,
headers,
status: res.status,
}
}

Expand Down
9 changes: 5 additions & 4 deletions packages/bigcommerce/src/api/utils/fetch-graphql-api.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import { FetcherError } from '@vercel/commerce/utils/errors'
import type { GraphQLFetcher } from '@vercel/commerce/api'
import type { FetchOptions, GraphQLFetcher } from '@vercel/commerce/api'
import type { BigcommerceConfig } from '../index'

const fetchGraphqlApi: (getConfig: () => BigcommerceConfig) => GraphQLFetcher =
(getConfig) =>
async (
query: string,
{ variables, preview } = {},
options: { headers?: HeadersInit } = {}
options?: FetchOptions
): Promise<any> => {
// log.warn(query)
const config = getConfig()

const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), {
method: 'POST',
method: options?.method || 'POST',
headers: {
Authorization: `Bearer ${config.apiToken}`,
...options.headers,
...options?.headers,
'Content-Type': 'application/json',
},
body: JSON.stringify({
...options?.body,
query,
variables,
}),
Expand Down
4 changes: 3 additions & 1 deletion packages/bigcommerce/src/api/utils/get-customer-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ async function getCustomerId({
getCustomerIdQuery,
undefined,
{
'Set-Cookie': `${config.customerCookie}=${customerToken}`,
headers: {
cookie: `${config.customerCookie}=${customerToken}`,
},
}
)

Expand Down
12 changes: 12 additions & 0 deletions packages/bigcommerce/src/api/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,15 @@ export type RecursivePartial<T> = {
export type RecursiveRequired<T> = {
[P in keyof T]-?: RecursiveRequired<T[P]>
}

export interface BCWishlist {
id: number
items: {
id: number
customer_id: number
is_public: boolean
product_id: number
variant_id: number
}[]
token: string
}
Loading

11 comments on commit d1d9e8c

@vercel
Copy link

@vercel vercel bot commented on d1d9e8c Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on d1d9e8c Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on d1d9e8c Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on d1d9e8c Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on d1d9e8c Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on d1d9e8c Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on d1d9e8c Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on d1d9e8c Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on d1d9e8c Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on d1d9e8c Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on d1d9e8c Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.