mirror of
https://github.com/vercel/commerce.git
synced 2025-06-19 05:31:22 +00:00
Start updates
This commit is contained in:
parent
03ad9ba902
commit
4d8e36e936
@ -6,7 +6,8 @@ import type { CustomerSchema } from '../types/customer'
|
||||
import type { LoginSchema } from '../types/login'
|
||||
import type { LogoutSchema } from '../types/logout'
|
||||
import type { SignupSchema } from '../types/signup'
|
||||
import type { ProductsSchema } from '@commerce/types/product'
|
||||
import type { ProductsSchema } from '../types/product'
|
||||
import type { WishlistSchema } from '../types/wishlist'
|
||||
import {
|
||||
defaultOperations,
|
||||
OPERATIONS,
|
||||
@ -21,6 +22,7 @@ export type APISchemas =
|
||||
| LogoutSchema
|
||||
| SignupSchema
|
||||
| ProductsSchema
|
||||
| WishlistSchema
|
||||
|
||||
export type GetAPISchema<
|
||||
C extends CommerceAPI<any>,
|
||||
@ -130,6 +132,18 @@ export function getEndpoint<
|
||||
}
|
||||
}
|
||||
|
||||
export const createEndpoint = <API extends GetAPISchema<any, any>>(
|
||||
endpoint: API['endpoint']
|
||||
) => <P extends APIProvider>(
|
||||
commerce: CommerceAPI<P>,
|
||||
context?: Partial<API['endpoint']> & {
|
||||
config?: P['config']
|
||||
options?: API['schema']['endpoint']['options']
|
||||
}
|
||||
): NextApiHandler => {
|
||||
return getEndpoint(commerce, { ...endpoint, ...context })
|
||||
}
|
||||
|
||||
export interface CommerceAPIConfig {
|
||||
locale?: string
|
||||
commerceUrl: string
|
||||
|
@ -1 +0,0 @@
|
||||
export default function () {}
|
@ -1 +0,0 @@
|
||||
export default function () {}
|
@ -1 +0,0 @@
|
||||
export default function () {}
|
@ -1,46 +0,0 @@
|
||||
import isAllowedMethod from '../utils/is-allowed-method'
|
||||
import createApiHandler, {
|
||||
ShopifyApiHandler,
|
||||
} from '../utils/create-api-handler'
|
||||
|
||||
import {
|
||||
SHOPIFY_CHECKOUT_ID_COOKIE,
|
||||
SHOPIFY_CHECKOUT_URL_COOKIE,
|
||||
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
||||
} from '../../const'
|
||||
|
||||
import { getConfig } from '..'
|
||||
import associateCustomerWithCheckoutMutation from '../../utils/mutations/associate-customer-with-checkout'
|
||||
|
||||
const METHODS = ['GET']
|
||||
|
||||
const checkoutApi: ShopifyApiHandler<any> = async (req, res, config) => {
|
||||
if (!isAllowedMethod(req, res, METHODS)) return
|
||||
|
||||
config = getConfig()
|
||||
|
||||
const { cookies } = req
|
||||
const checkoutUrl = cookies[SHOPIFY_CHECKOUT_URL_COOKIE]
|
||||
const customerCookie = cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE]
|
||||
|
||||
if (customerCookie) {
|
||||
try {
|
||||
await config.fetch(associateCustomerWithCheckoutMutation, {
|
||||
variables: {
|
||||
checkoutId: cookies[SHOPIFY_CHECKOUT_ID_COOKIE],
|
||||
customerAccessToken: cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE],
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
if (checkoutUrl) {
|
||||
res.redirect(checkoutUrl)
|
||||
} else {
|
||||
res.redirect('/cart')
|
||||
}
|
||||
}
|
||||
|
||||
export default createApiHandler(checkoutApi, {}, {})
|
@ -1 +0,0 @@
|
||||
export default function () {}
|
@ -1 +0,0 @@
|
||||
export default function () {}
|
@ -1 +0,0 @@
|
||||
export default function () {}
|
@ -1 +0,0 @@
|
||||
export default function () {}
|
@ -1 +0,0 @@
|
||||
export default function () {}
|
@ -1,4 +1,9 @@
|
||||
import type { CommerceAPIConfig } from '@commerce/api'
|
||||
import {
|
||||
CommerceAPI,
|
||||
CommerceAPIConfig,
|
||||
getCommerceApi as commerceApi,
|
||||
getEndpoint,
|
||||
} from '@commerce/api'
|
||||
|
||||
import {
|
||||
API_URL,
|
||||
@ -7,6 +12,11 @@ import {
|
||||
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
||||
} from '../const'
|
||||
|
||||
import fetchGraphqlApi from './utils/fetch-graphql-api'
|
||||
|
||||
import getSiteInfo from './operations/get-site-info'
|
||||
import { NextApiHandler } from 'next'
|
||||
|
||||
if (!API_URL) {
|
||||
throw new Error(
|
||||
`The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store`
|
||||
@ -18,44 +28,26 @@ if (!API_TOKEN) {
|
||||
`The environment variable NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store`
|
||||
)
|
||||
}
|
||||
|
||||
import fetchGraphqlApi from './utils/fetch-graphql-api'
|
||||
|
||||
export interface ShopifyConfig extends CommerceAPIConfig {}
|
||||
|
||||
export class Config {
|
||||
private config: ShopifyConfig
|
||||
|
||||
constructor(config: ShopifyConfig) {
|
||||
this.config = config
|
||||
}
|
||||
|
||||
getConfig(userConfig: Partial<ShopifyConfig> = {}) {
|
||||
return Object.entries(userConfig).reduce<ShopifyConfig>(
|
||||
(cfg, [key, value]) => Object.assign(cfg, { [key]: value }),
|
||||
{ ...this.config }
|
||||
)
|
||||
}
|
||||
|
||||
setConfig(newConfig: Partial<ShopifyConfig>) {
|
||||
Object.assign(this.config, newConfig)
|
||||
}
|
||||
export interface ShopifyConfig extends CommerceAPIConfig {
|
||||
applyLocale?: boolean
|
||||
}
|
||||
|
||||
const config = new Config({
|
||||
locale: 'en-US',
|
||||
const ONE_DAY = 60 * 60 * 24
|
||||
const config: ShopifyConfig = {
|
||||
commerceUrl: API_URL,
|
||||
apiToken: API_TOKEN!,
|
||||
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
|
||||
cartCookieMaxAge: 60 * 60 * 24 * 30,
|
||||
fetch: fetchGraphqlApi,
|
||||
apiToken: API_TOKEN,
|
||||
customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
||||
})
|
||||
|
||||
export function getConfig(userConfig?: Partial<ShopifyConfig>) {
|
||||
return config.getConfig(userConfig)
|
||||
cartCookie: process.env.SHOPIFY_CART_COOKIE ?? 'shopify_checkoutId',
|
||||
cartCookieMaxAge: ONE_DAY * 30,
|
||||
fetch: fetchGraphqlApi,
|
||||
applyLocale: true,
|
||||
}
|
||||
|
||||
export function setConfig(newConfig: Partial<ShopifyConfig>) {
|
||||
return config.setConfig(newConfig)
|
||||
export const provider = {
|
||||
config: config,
|
||||
operations: { getSiteInfo },
|
||||
}
|
||||
|
||||
export type Provider = typeof provider
|
||||
|
||||
export default provider
|
||||
|
36
framework/shopify/api/operations/get-site-info.ts
Normal file
36
framework/shopify/api/operations/get-site-info.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import type { OperationContext } from '@commerce/api/operations'
|
||||
|
||||
import type { GetSiteInfoQuery } from '../../schema'
|
||||
|
||||
import type { ShopifyConfig, Provider } from '..'
|
||||
import { GetSiteInfoOperation } from '../../types/site'
|
||||
|
||||
import getSiteInfoQuery from '../../utils/queries/get-site-info-query'
|
||||
import { getCategories, getVendors } from '@framework/utils'
|
||||
|
||||
export default function getSiteInfoOperation({
|
||||
commerce,
|
||||
}: OperationContext<Provider>) {
|
||||
async function getSiteInfo<T extends GetSiteInfoOperation>({
|
||||
query = getSiteInfoQuery,
|
||||
config,
|
||||
}: {
|
||||
query?: string
|
||||
config?: ShopifyConfig
|
||||
preview?: boolean
|
||||
} = {}): Promise<T['data']> {
|
||||
config = commerce.getConfig(config)
|
||||
|
||||
const categories = await getCategories(config)
|
||||
const brands = await getVendors(config)
|
||||
|
||||
const { data } = await config.fetch<GetSiteInfoQuery>(query)
|
||||
|
||||
return {
|
||||
categories,
|
||||
brands,
|
||||
}
|
||||
}
|
||||
|
||||
return getSiteInfo
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
|
||||
import { ShopifyConfig, getConfig } from '..'
|
||||
|
||||
export type ShopifyApiHandler<
|
||||
T = any,
|
||||
H extends ShopifyHandlers = {},
|
||||
Options extends {} = {}
|
||||
> = (
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<ShopifyApiResponse<T>>,
|
||||
config: ShopifyConfig,
|
||||
handlers: H,
|
||||
// Custom configs that may be used by a particular handler
|
||||
options: Options
|
||||
) => void | Promise<void>
|
||||
|
||||
export type ShopifyHandler<T = any, Body = null> = (options: {
|
||||
req: NextApiRequest
|
||||
res: NextApiResponse<ShopifyApiResponse<T>>
|
||||
config: ShopifyConfig
|
||||
body: Body
|
||||
}) => void | Promise<void>
|
||||
|
||||
export type ShopifyHandlers<T = any> = {
|
||||
[k: string]: ShopifyHandler<T, any>
|
||||
}
|
||||
|
||||
export type ShopifyApiResponse<T> = {
|
||||
data: T | null
|
||||
errors?: { message: string; code?: string }[]
|
||||
}
|
||||
|
||||
export default function createApiHandler<
|
||||
T = any,
|
||||
H extends ShopifyHandlers = {},
|
||||
Options extends {} = {}
|
||||
>(
|
||||
handler: ShopifyApiHandler<T, H, Options>,
|
||||
handlers: H,
|
||||
defaultOptions: Options
|
||||
) {
|
||||
return function getApiHandler({
|
||||
config,
|
||||
operations,
|
||||
options,
|
||||
}: {
|
||||
config?: ShopifyConfig
|
||||
operations?: Partial<H>
|
||||
options?: Options extends {} ? Partial<Options> : never
|
||||
} = {}): NextApiHandler {
|
||||
const ops = { ...operations, ...handlers }
|
||||
const opts = { ...defaultOptions, ...options }
|
||||
|
||||
return function apiHandler(req, res) {
|
||||
return handler(req, res, getConfig(config), ops, opts)
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
export type WishlistItem = { product: any; id: number }
|
||||
export default function () {}
|
6
framework/shopify/schema.d.ts
vendored
6
framework/shopify/schema.d.ts
vendored
@ -5578,3 +5578,9 @@ export type GetProductBySlugQuery = { __typename?: 'QueryRoot' } & {
|
||||
}
|
||||
>
|
||||
}
|
||||
|
||||
export type GetSiteInfoQueryVariables = Exact<{ [key: string]: never }>
|
||||
|
||||
export type GetSiteInfoQuery = { __typename?: 'QueryRoot' } & {
|
||||
shop: { __typename?: 'Shop' } & Pick<Shop, 'name'>
|
||||
}
|
||||
|
@ -13,6 +13,16 @@ directive @accessRestricted(
|
||||
reason: String = null
|
||||
) on FIELD_DEFINITION | OBJECT
|
||||
|
||||
"""
|
||||
Contextualize data.
|
||||
"""
|
||||
directive @inContext(
|
||||
"""
|
||||
The country code for context.
|
||||
"""
|
||||
country: CountryCode!
|
||||
) on QUERY | MUTATION
|
||||
|
||||
"""
|
||||
A version of the API.
|
||||
"""
|
||||
|
@ -1,45 +0,0 @@
|
||||
import * as Core from '@commerce/types'
|
||||
import { CheckoutLineItem } from './schema'
|
||||
|
||||
export type ShopifyCheckout = {
|
||||
id: string
|
||||
webUrl: string
|
||||
lineItems: CheckoutLineItem[]
|
||||
}
|
||||
|
||||
export type Cart = Core.Cart & {
|
||||
lineItems: LineItem[]
|
||||
url?: String
|
||||
}
|
||||
|
||||
export interface LineItem extends Core.LineItem {
|
||||
options?: any[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Cart mutations
|
||||
*/
|
||||
|
||||
export type OptionSelections = {
|
||||
option_id: number
|
||||
option_value: number | string
|
||||
}
|
||||
|
||||
export type CartItemBody = Core.CartItemBody & {
|
||||
productId: string // The product id is always required for BC
|
||||
optionSelections?: OptionSelections
|
||||
}
|
||||
|
||||
export type GetCartHandlerBody = Core.GetCartHandlerBody
|
||||
|
||||
export type AddCartItemBody = Core.AddCartItemBody<CartItemBody>
|
||||
|
||||
export type AddCartItemHandlerBody = Core.AddCartItemHandlerBody<CartItemBody>
|
||||
|
||||
export type UpdateCartItemBody = Core.UpdateCartItemBody<CartItemBody>
|
||||
|
||||
export type UpdateCartItemHandlerBody = Core.UpdateCartItemHandlerBody<CartItemBody>
|
||||
|
||||
export type RemoveCartItemBody = Core.RemoveCartItemBody
|
||||
|
||||
export type RemoveCartItemHandlerBody = Core.RemoveCartItemHandlerBody
|
45
framework/shopify/types/cart.ts
Normal file
45
framework/shopify/types/cart.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import * as Core from '@commerce/types/cart'
|
||||
|
||||
export * from '@commerce/types/cart'
|
||||
|
||||
export type ShopifyCart = {}
|
||||
|
||||
/**
|
||||
* Extend core cart types
|
||||
*/
|
||||
|
||||
export type Cart = Core.Cart & {
|
||||
lineItems: Core.LineItem[]
|
||||
}
|
||||
|
||||
export type OptionSelections = {
|
||||
option_id: number
|
||||
option_value: number | string
|
||||
}
|
||||
|
||||
export type CartItemBody = Core.CartItemBody & {
|
||||
productId: string // The product id is always required for BC
|
||||
optionSelections?: OptionSelections
|
||||
}
|
||||
|
||||
export type CartTypes = {
|
||||
cart: Cart
|
||||
item: Core.LineItem
|
||||
itemBody: CartItemBody
|
||||
}
|
||||
|
||||
export type CartHooks = Core.CartHooks<CartTypes>
|
||||
|
||||
export type GetCartHook = CartHooks['getCart']
|
||||
export type AddItemHook = CartHooks['addItem']
|
||||
export type UpdateItemHook = CartHooks['updateItem']
|
||||
export type RemoveItemHook = CartHooks['removeItem']
|
||||
|
||||
export type CartSchema = Core.CartSchema<CartTypes>
|
||||
|
||||
export type CartHandlers = Core.CartHandlers<CartTypes>
|
||||
|
||||
export type GetCartHandler = CartHandlers['getCart']
|
||||
export type AddItemHandler = CartHandlers['addItem']
|
||||
export type UpdateItemHandler = CartHandlers['updateItem']
|
||||
export type RemoveItemHandler = CartHandlers['removeItem']
|
5
framework/shopify/types/customer.ts
Normal file
5
framework/shopify/types/customer.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import * as Core from '@commerce/types/customer'
|
||||
|
||||
export * from '@commerce/types/customer'
|
||||
|
||||
export type CustomerSchema = Core.CustomerSchema
|
2
framework/shopify/types/index.ts
Normal file
2
framework/shopify/types/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from '@commerce/types'
|
||||
export * from './cart'
|
8
framework/shopify/types/login.ts
Normal file
8
framework/shopify/types/login.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import * as Core from '@commerce/types/login'
|
||||
import type { LoginMutationVariables } from '../schema'
|
||||
|
||||
export * from '@commerce/types/login'
|
||||
|
||||
export type LoginOperation = Core.LoginOperation & {
|
||||
variables: LoginMutationVariables
|
||||
}
|
1
framework/shopify/types/logout.ts
Normal file
1
framework/shopify/types/logout.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from '@commerce/types/logout'
|
13
framework/shopify/types/page.ts
Normal file
13
framework/shopify/types/page.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import * as Core from '@commerce/types/page'
|
||||
import { definitions } from '../api/definitions/store-content'
|
||||
|
||||
export * from '@commerce/types/page'
|
||||
|
||||
export type Page = definitions['page_Full']
|
||||
|
||||
export type PageTypes = {
|
||||
page: Page
|
||||
}
|
||||
|
||||
export type GetAllPagesOperation = Core.GetAllPagesOperation<PageTypes>
|
||||
export type GetPageOperation = Core.GetPageOperation<PageTypes>
|
1
framework/shopify/types/product.ts
Normal file
1
framework/shopify/types/product.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from '@commerce/types/product'
|
1
framework/shopify/types/signup.ts
Normal file
1
framework/shopify/types/signup.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from '@commerce/types/signup'
|
1
framework/shopify/types/site.ts
Normal file
1
framework/shopify/types/site.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from '@commerce/types/site'
|
8
framework/shopify/utils/queries/get-site-info-query.ts
Normal file
8
framework/shopify/utils/queries/get-site-info-query.ts
Normal file
@ -0,0 +1,8 @@
|
||||
const getSiteInfoQuery = /* GraphQL */ `
|
||||
query getSiteInfo {
|
||||
shop {
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
export default getSiteInfoQuery
|
@ -19,8 +19,9 @@ export async function getStaticProps({
|
||||
config,
|
||||
preview,
|
||||
})
|
||||
|
||||
console.log(commerce)
|
||||
const { categories, brands } = await commerce.getSiteInfo({ config, preview })
|
||||
|
||||
const { pages } = await commerce.getAllPages({ config, preview })
|
||||
|
||||
return {
|
||||
|
@ -22,8 +22,8 @@
|
||||
"@components/*": ["components/*"],
|
||||
"@commerce": ["framework/commerce"],
|
||||
"@commerce/*": ["framework/commerce/*"],
|
||||
"@framework": ["framework/bigcommerce"],
|
||||
"@framework/*": ["framework/bigcommerce/*"]
|
||||
"@framework": ["framework/shopify"],
|
||||
"@framework/*": ["framework/shopify/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],
|
||||
|
Loading…
x
Reference in New Issue
Block a user