Merge branch 'nodejs-provider' of https://github.com/vercel/commerce into shopify-updates

This commit is contained in:
cond0r 2021-05-24 09:40:11 +03:00
commit 184d1ca0f2
28 changed files with 350 additions and 71 deletions

View File

@ -1,4 +1,5 @@
import type { GetAPISchema } from '@commerce/api' import { GetAPISchema, createEndpoint } from '@commerce/api'
import cartEndpoint from '@commerce/api/endpoints/cart'
import type { CartSchema } from '../../../types/cart' import type { CartSchema } from '../../../types/cart'
import type { BigcommerceAPI } from '../..' import type { BigcommerceAPI } from '../..'
import getCart from './get-cart' import getCart from './get-cart'
@ -10,4 +11,16 @@ export type CartAPI = GetAPISchema<BigcommerceAPI, CartSchema>
export type CartEndpoint = CartAPI['endpoint'] export type CartEndpoint = CartAPI['endpoint']
export const handlers = { getCart, addItem, updateItem, removeItem } export const handlers: CartEndpoint['handlers'] = {
getCart,
addItem,
updateItem,
removeItem,
}
const cartApi = createEndpoint<CartAPI>({
handler: cartEndpoint,
handlers,
})
export default cartApi

View File

@ -1,4 +1,5 @@
import type { GetAPISchema } from '@commerce/api' import { GetAPISchema, createEndpoint } from '@commerce/api'
import customerEndpoint from '@commerce/api/endpoints/customer'
import type { CustomerSchema } from '../../../types/customer' import type { CustomerSchema } from '../../../types/customer'
import type { BigcommerceAPI } from '../..' import type { BigcommerceAPI } from '../..'
import getLoggedInCustomer from './get-logged-in-customer' import getLoggedInCustomer from './get-logged-in-customer'
@ -7,4 +8,11 @@ export type CustomerAPI = GetAPISchema<BigcommerceAPI, CustomerSchema>
export type CustomerEndpoint = CustomerAPI['endpoint'] export type CustomerEndpoint = CustomerAPI['endpoint']
export const handlers = { getLoggedInCustomer } export const handlers: CustomerEndpoint['handlers'] = { getLoggedInCustomer }
const customerApi = createEndpoint<CustomerAPI>({
handler: customerEndpoint,
handlers,
})
export default customerApi

View File

@ -1,4 +1,5 @@
import type { GetAPISchema } from '@commerce/api' import { GetAPISchema, createEndpoint } from '@commerce/api'
import loginEndpoint from '@commerce/api/endpoints/login'
import type { LoginSchema } from '../../../types/login' import type { LoginSchema } from '../../../types/login'
import type { BigcommerceAPI } from '../..' import type { BigcommerceAPI } from '../..'
import login from './login' import login from './login'
@ -7,4 +8,11 @@ export type LoginAPI = GetAPISchema<BigcommerceAPI, LoginSchema>
export type LoginEndpoint = LoginAPI['endpoint'] export type LoginEndpoint = LoginAPI['endpoint']
export const handlers = { login } export const handlers: LoginEndpoint['handlers'] = { login }
const loginApi = createEndpoint<LoginAPI>({
handler: loginEndpoint,
handlers,
})
export default loginApi

View File

@ -1,4 +1,5 @@
import type { GetAPISchema } from '@commerce/api' import { GetAPISchema, createEndpoint } from '@commerce/api'
import logoutEndpoint from '@commerce/api/endpoints/logout'
import type { LogoutSchema } from '../../../types/logout' import type { LogoutSchema } from '../../../types/logout'
import type { BigcommerceAPI } from '../..' import type { BigcommerceAPI } from '../..'
import logout from './logout' import logout from './logout'
@ -7,4 +8,11 @@ export type LogoutAPI = GetAPISchema<BigcommerceAPI, LogoutSchema>
export type LogoutEndpoint = LogoutAPI['endpoint'] export type LogoutEndpoint = LogoutAPI['endpoint']
export const handlers = { logout } export const handlers: LogoutEndpoint['handlers'] = { logout }
const logoutApi = createEndpoint<LogoutAPI>({
handler: logoutEndpoint,
handlers,
})
export default logoutApi

View File

@ -1,4 +1,5 @@
import type { GetAPISchema } from '@commerce/api' import { GetAPISchema, createEndpoint } from '@commerce/api'
import signupEndpoint from '@commerce/api/endpoints/signup'
import type { SignupSchema } from '../../../types/signup' import type { SignupSchema } from '../../../types/signup'
import type { BigcommerceAPI } from '../..' import type { BigcommerceAPI } from '../..'
import signup from './signup' import signup from './signup'
@ -7,4 +8,11 @@ export type SignupAPI = GetAPISchema<BigcommerceAPI, SignupSchema>
export type SignupEndpoint = SignupAPI['endpoint'] export type SignupEndpoint = SignupAPI['endpoint']
export const handlers = { signup } export const handlers: SignupEndpoint['handlers'] = { signup }
const singupApi = createEndpoint<SignupAPI>({
handler: signupEndpoint,
handlers,
})
export default singupApi

View File

@ -0,0 +1,56 @@
import getCustomerWishlist from '../../../customer/get-customer-wishlist'
import { parseWishlistItem } from '../../utils/parse-item'
import getCustomerId from './utils/get-customer-id'
import type { WishlistEndpoint } from '.'
// Return wishlist info
const addItem: WishlistEndpoint['handlers']['addItem'] = async ({
res,
body: { customerToken, item },
config,
}) => {
if (!item) {
return res.status(400).json({
data: null,
errors: [{ message: 'Missing item' }],
})
}
const customerId =
customerToken && (await getCustomerId({ customerToken, config }))
if (!customerId) {
return res.status(400).json({
data: null,
errors: [{ message: 'Invalid request' }],
})
}
const { wishlist } = await getCustomerWishlist({
variables: { customerId },
config,
})
const options = {
method: 'POST',
body: JSON.stringify(
wishlist
? {
items: [parseWishlistItem(item)],
}
: {
name: 'Wishlist',
customer_id: customerId,
items: [parseWishlistItem(item)],
is_public: false,
}
),
}
const { data } = wishlist
? await config.storeApiFetch(`/v3/wishlists/${wishlist.id}/items`, options)
: await config.storeApiFetch('/v3/wishlists', options)
res.status(200).json({ data })
}
export default addItem

View File

@ -0,0 +1,38 @@
import type { Wishlist } from '../../../types/wishlist'
import type { WishlistEndpoint } from '.'
import getCustomerId from './utils/get-customer-id'
import getCustomerWishlist from '../../../customer/get-customer-wishlist'
// Return wishlist info
const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({
res,
body: { customerToken, includeProducts },
config,
}) => {
let result: { data?: Wishlist } = {}
if (customerToken) {
const customerId =
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' }],
})
}
const { wishlist } = await getCustomerWishlist({
variables: { customerId },
includeProducts,
config,
})
result = { data: wishlist }
}
res.status(200).json({ data: result.data ?? null })
}
export default getWishlist

View File

@ -0,0 +1,24 @@
import { GetAPISchema, createEndpoint } from '@commerce/api'
import wishlistEndpoint from '@commerce/api/endpoints/wishlist'
import type { WishlistSchema } from '../../../types/wishlist'
import type { BigcommerceAPI } from '../..'
import getWishlist from './get-wishlist'
import addItem from './add-item'
import removeItem from './remove-item'
export type WishlistAPI = GetAPISchema<BigcommerceAPI, WishlistSchema>
export type WishlistEndpoint = WishlistAPI['endpoint']
export const handlers: WishlistEndpoint['handlers'] = {
getWishlist,
addItem,
removeItem,
}
const wishlistApi = createEndpoint<WishlistAPI>({
handler: wishlistEndpoint,
handlers,
})
export default wishlistApi

View File

@ -0,0 +1,38 @@
import type { Wishlist } from '../../../types/wishlist'
import getCustomerWishlist from '../../../customer/get-customer-wishlist'
import getCustomerId from './utils/get-customer-id'
import type { WishlistEndpoint } from '.'
// Return wishlist info
const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({
res,
body: { customerToken, itemId },
config,
}) => {
const customerId =
customerToken && (await getCustomerId({ customerToken, config }))
const { wishlist } =
(customerId &&
(await getCustomerWishlist({
variables: { customerId },
config,
}))) ||
{}
if (!wishlist || !itemId) {
return res.status(400).json({
data: null,
errors: [{ message: 'Invalid request' }],
})
}
const result = await config.storeApiFetch<{ data: Wishlist } | null>(
`/v3/wishlists/${wishlist.id}/items/${itemId}`,
{ method: 'DELETE' }
)
const data = result?.data ?? null
res.status(200).json({ data })
}
export default removeItem

View File

@ -1,5 +1,5 @@
import { GetCustomerIdQuery } from '../schema' import type { GetCustomerIdQuery } from '../../../../schema'
import { BigcommerceConfig, getConfig } from '../api' import type { BigcommerceConfig } from '../../..'
export const getCustomerIdQuery = /* GraphQL */ ` export const getCustomerIdQuery = /* GraphQL */ `
query getCustomerId { query getCustomerId {
@ -14,10 +14,8 @@ async function getCustomerId({
config, config,
}: { }: {
customerToken: string customerToken: string
config?: BigcommerceConfig config: BigcommerceConfig
}): Promise<number | undefined> { }): Promise<number | undefined> {
config = getConfig(config)
const { data } = await config.fetch<GetCustomerIdQuery>( const { data } = await config.fetch<GetCustomerIdQuery>(
getCustomerIdQuery, getCustomerIdQuery,
undefined, undefined,

View File

@ -4,7 +4,6 @@ import {
CommerceAPI, CommerceAPI,
CommerceAPIConfig, CommerceAPIConfig,
getCommerceApi as commerceApi, getCommerceApi as commerceApi,
getEndpoint,
} from '@commerce/api' } from '@commerce/api'
import fetchGraphqlApi from './utils/fetch-graphql-api' import fetchGraphqlApi from './utils/fetch-graphql-api'
import fetchStoreApi from './utils/fetch-store-api' import fetchStoreApi from './utils/fetch-store-api'
@ -15,6 +14,7 @@ import type { LoginAPI } from './endpoints/login'
import type { LogoutAPI } from './endpoints/logout' import type { LogoutAPI } from './endpoints/logout'
import type { SignupAPI } from './endpoints/signup' import type { SignupAPI } from './endpoints/signup'
import type { ProductsAPI } from './endpoints/catalog/products' import type { ProductsAPI } from './endpoints/catalog/products'
import type { WishlistAPI } from './endpoints/wishlist'
import login from './operations/login' import login from './operations/login'
import getAllPages from './operations/get-all-pages' import getAllPages from './operations/get-all-pages'
@ -127,24 +127,14 @@ export type APIs =
| LogoutAPI | LogoutAPI
| SignupAPI | SignupAPI
| ProductsAPI | ProductsAPI
| WishlistAPI
export type BigcommerceAPI<P extends Provider = Provider> = CommerceAPI<P> export type BigcommerceAPI<P extends Provider = Provider> = CommerceAPI<P>
export function getCommerceApi<P extends Provider>( export function getCommerceApi<P extends Provider>(
customProvider: P = provider as any customProvider: P = provider as any
) { ): BigcommerceAPI<P> {
const api: BigcommerceAPI<P> = commerceApi(customProvider) return commerceApi(customProvider)
return Object.assign(api, {
endpoint<E extends APIs>(
context: E['endpoint'] & {
config?: P['config']
options?: E['schema']['endpoint']['options']
}
): NextApiHandler {
return getEndpoint(api, context)
},
})
} }
export function getConfig(userConfig?: Partial<BigcommerceConfig>) { export function getConfig(userConfig?: Partial<BigcommerceConfig>) {

View File

@ -1,5 +1,5 @@
import type { WishlistHandlers } from '..' import type { WishlistHandlers } from '..'
import getCustomerId from '../../../customer/get-customer-id' import getCustomerId from '../../endpoints/wishlist/utils/get-customer-id'
import getCustomerWishlist from '../../../customer/get-customer-wishlist' import getCustomerWishlist from '../../../customer/get-customer-wishlist'
import { parseWishlistItem } from '../../utils/parse-item' import { parseWishlistItem } from '../../utils/parse-item'

View File

@ -1,4 +1,4 @@
import getCustomerId from '../../../customer/get-customer-id' import getCustomerId from '../../endpoints/wishlist/utils/get-customer-id'
import getCustomerWishlist from '../../../customer/get-customer-wishlist' import getCustomerWishlist from '../../../customer/get-customer-wishlist'
import type { Wishlist, WishlistHandlers } from '..' import type { Wishlist, WishlistHandlers } from '..'

View File

@ -1,4 +1,4 @@
import getCustomerId from '../../../customer/get-customer-id' import getCustomerId from '../../endpoints/wishlist/utils/get-customer-id'
import getCustomerWishlist, { import getCustomerWishlist, {
Wishlist, Wishlist,
} from '../../../customer/get-customer-wishlist' } from '../../../customer/get-customer-wishlist'

View File

@ -0,0 +1,22 @@
import * as Core from '@commerce/types/wishlist'
import { definitions } from '../api/definitions/wishlist'
import type { ProductEdge } from '../product/get-all-products'
export * from '@commerce/types/wishlist'
export type WishlistItem = NonNullable<
definitions['wishlist_Full']['items']
>[0] & {
product?: ProductEdge['node']
}
export type Wishlist = Omit<definitions['wishlist_Full'], 'items'> & {
items?: WishlistItem[]
}
export type WishlistTypes = {
wishlist: Wishlist
itemBody: Core.WishlistItemBody
}
export type WishlistSchema = Core.WishlistSchema<WishlistTypes>

View File

@ -5,7 +5,7 @@ import type { GetAPISchema } from '..'
const cartEndpoint: GetAPISchema< const cartEndpoint: GetAPISchema<
any, any,
CartSchema CartSchema<any>
>['endpoint']['handler'] = async (ctx) => { >['endpoint']['handler'] = async (ctx) => {
const { req, res, handlers, config } = ctx const { req, res, handlers, config } = ctx

View File

@ -5,7 +5,7 @@ import type { GetAPISchema } from '..'
const customerEndpoint: GetAPISchema< const customerEndpoint: GetAPISchema<
any, any,
CustomerSchema CustomerSchema<any>
>['endpoint']['handler'] = async (ctx) => { >['endpoint']['handler'] = async (ctx) => {
const { req, res, handlers } = ctx const { req, res, handlers } = ctx

View File

@ -5,7 +5,7 @@ import type { GetAPISchema } from '..'
const loginEndpoint: GetAPISchema< const loginEndpoint: GetAPISchema<
any, any,
LoginSchema LoginSchema<any>
>['endpoint']['handler'] = async (ctx) => { >['endpoint']['handler'] = async (ctx) => {
const { req, res, handlers } = ctx const { req, res, handlers } = ctx

View File

@ -0,0 +1,58 @@
import type { WishlistSchema } from '../../types/wishlist'
import { CommerceAPIError } from '../utils/errors'
import isAllowedOperation from '../utils/is-allowed-operation'
import type { GetAPISchema } from '..'
const wishlistEndpoint: GetAPISchema<
any,
WishlistSchema<any>
>['endpoint']['handler'] = async (ctx) => {
const { req, res, handlers, config } = ctx
if (
!isAllowedOperation(req, res, {
GET: handlers['getWishlist'],
POST: handlers['addItem'],
DELETE: handlers['removeItem'],
})
) {
return
}
const { cookies } = req
const customerToken = cookies[config.customerCookie]
try {
// Return current wishlist info
if (req.method === 'GET') {
const body = {
customerToken,
includeProducts: req.query.products === '1',
}
return await handlers['getWishlist']({ ...ctx, body })
}
// 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 }] })
}
}
export default wishlistEndpoint

View File

@ -0,0 +1,32 @@
// TODO: define this type
export type Wishlist = any
export type WishlistItemBody = {
variantId: string
productId: string
}
export type WishlistTypes = {
wishlist: Wishlist
itemBody: WishlistItemBody
}
export type WishlistSchema<T extends WishlistTypes = WishlistTypes> = {
endpoint: {
options: {}
handlers: {
getWishlist: {
data: T['wishlist'] | null
body: { customerToken?: string; includeProducts?: boolean }
}
addItem: {
data: T['wishlist']
body: { customerToken?: string; item: T['itemBody'] }
}
removeItem: {
data: T['wishlist'] | null
body: { customerToken?: string; itemId: string }
}
}
}
}

View File

@ -1,3 +0,0 @@
import catalogProductsApi from '@framework/api/catalog/products'
export default catalogProductsApi()

View File

@ -1,3 +0,0 @@
import wishlistApi from '@framework/api/wishlist'
export default wishlistApi()

View File

@ -1,8 +1,4 @@
import cart from '@commerce/api/endpoints/cart' import cartApi from '@framework/api/endpoints/cart'
import { CartAPI, handlers } from '@framework/api/endpoints/cart'
import commerce from '@lib/api/commerce' import commerce from '@lib/api/commerce'
export default commerce.endpoint({ export default cartApi(commerce)
handler: cart as CartAPI['endpoint']['handler'],
handlers,
})

View File

@ -1,8 +1,4 @@
import customer from '@commerce/api/endpoints/customer' import customerApi from '@framework/api/endpoints/customer'
import { CustomerAPI, handlers } from '@framework/api/endpoints/customer'
import commerce from '@lib/api/commerce' import commerce from '@lib/api/commerce'
export default commerce.endpoint({ export default customerApi(commerce)
handler: customer as CustomerAPI['endpoint']['handler'],
handlers,
})

View File

@ -1,8 +1,4 @@
import login from '@commerce/api/endpoints/login' import loginApi from '@framework/api/endpoints/login'
import { LoginAPI, handlers } from '@framework/api/endpoints/login'
import commerce from '@lib/api/commerce' import commerce from '@lib/api/commerce'
export default commerce.endpoint({ export default loginApi(commerce)
handler: login as LoginAPI['endpoint']['handler'],
handlers,
})

View File

@ -1,8 +1,4 @@
import logout from '@commerce/api/endpoints/logout' import logoutApi from '@framework/api/endpoints/logout'
import { LogoutAPI, handlers } from '@framework/api/endpoints/logout'
import commerce from '@lib/api/commerce' import commerce from '@lib/api/commerce'
export default commerce.endpoint({ export default logoutApi(commerce)
handler: logout as LogoutAPI['endpoint']['handler'],
handlers,
})

View File

@ -1,8 +1,4 @@
import signup from '@commerce/api/endpoints/signup' import singupApi from '@framework/api/endpoints/signup'
import { SignupAPI, handlers } from '@framework/api/endpoints/signup'
import commerce from '@lib/api/commerce' import commerce from '@lib/api/commerce'
export default commerce.endpoint({ export default singupApi(commerce)
handler: signup as SignupAPI['endpoint']['handler'],
handlers,
})

4
pages/api/wishlist.ts Normal file
View File

@ -0,0 +1,4 @@
import wishlistApi from '@framework/api/endpoints/wishlist'
import commerce from '@lib/api/commerce'
export default wishlistApi(commerce)