mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 20:26:49 +00:00
Add swell provider folder
This commit is contained in:
1
framework/swell/api/cart/index.ts
Normal file
1
framework/swell/api/cart/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export default function () {}
|
1
framework/swell/api/catalog/index.ts
Normal file
1
framework/swell/api/catalog/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export default function () {}
|
1
framework/swell/api/catalog/products.ts
Normal file
1
framework/swell/api/catalog/products.ts
Normal file
@@ -0,0 +1 @@
|
||||
export default function () {}
|
46
framework/swell/api/checkout/index.ts
Normal file
46
framework/swell/api/checkout/index.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
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
framework/swell/api/customer.ts
Normal file
1
framework/swell/api/customer.ts
Normal file
@@ -0,0 +1 @@
|
||||
export default function () {}
|
1
framework/swell/api/customers/index.ts
Normal file
1
framework/swell/api/customers/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export default function () {}
|
1
framework/swell/api/customers/login.ts
Normal file
1
framework/swell/api/customers/login.ts
Normal file
@@ -0,0 +1 @@
|
||||
export default function () {}
|
1
framework/swell/api/customers/logout.ts
Normal file
1
framework/swell/api/customers/logout.ts
Normal file
@@ -0,0 +1 @@
|
||||
export default function () {}
|
1
framework/swell/api/customers/signup.ts
Normal file
1
framework/swell/api/customers/signup.ts
Normal file
@@ -0,0 +1 @@
|
||||
export default function () {}
|
62
framework/swell/api/index.ts
Normal file
62
framework/swell/api/index.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import type { CommerceAPIConfig } from '@commerce/api'
|
||||
|
||||
import {
|
||||
API_URL,
|
||||
API_TOKEN,
|
||||
SHOPIFY_CHECKOUT_ID_COOKIE,
|
||||
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
||||
SHOPIFY_COOKIE_EXPIRE,
|
||||
} from '../const'
|
||||
|
||||
if (!API_URL) {
|
||||
throw new Error(
|
||||
`The environment variable NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN is missing and it's required to access your store`
|
||||
)
|
||||
}
|
||||
|
||||
if (!API_TOKEN) {
|
||||
throw new Error(
|
||||
`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)
|
||||
}
|
||||
}
|
||||
|
||||
const config = new Config({
|
||||
locale: 'en-US',
|
||||
commerceUrl: API_URL,
|
||||
apiToken: API_TOKEN!,
|
||||
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
|
||||
cartCookieMaxAge: SHOPIFY_COOKIE_EXPIRE,
|
||||
fetch: fetchGraphqlApi,
|
||||
customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
||||
})
|
||||
|
||||
export function getConfig(userConfig?: Partial<ShopifyConfig>) {
|
||||
return config.getConfig(userConfig)
|
||||
}
|
||||
|
||||
export function setConfig(newConfig: Partial<ShopifyConfig>) {
|
||||
return config.setConfig(newConfig)
|
||||
}
|
21
framework/swell/api/operations/get-all-collections.ts
Normal file
21
framework/swell/api/operations/get-all-collections.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import Client from 'shopify-buy'
|
||||
import { ShopifyConfig } from '../index'
|
||||
|
||||
type Options = {
|
||||
config: ShopifyConfig
|
||||
}
|
||||
|
||||
const getAllCollections = async (options: Options) => {
|
||||
const { config } = options
|
||||
|
||||
const client = Client.buildClient({
|
||||
storefrontAccessToken: config.apiToken,
|
||||
domain: config.commerceUrl,
|
||||
})
|
||||
|
||||
const res = await client.collection.fetchAllWithProducts()
|
||||
|
||||
return JSON.parse(JSON.stringify(res))
|
||||
}
|
||||
|
||||
export default getAllCollections
|
25
framework/swell/api/operations/get-page.ts
Normal file
25
framework/swell/api/operations/get-page.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Page } from '../../schema'
|
||||
import { ShopifyConfig, getConfig } from '..'
|
||||
|
||||
export type GetPageResult<T extends { page?: any } = { page?: Page }> = T
|
||||
|
||||
export type PageVariables = {
|
||||
id: string
|
||||
}
|
||||
|
||||
async function getPage({
|
||||
url,
|
||||
variables,
|
||||
config,
|
||||
preview,
|
||||
}: {
|
||||
url?: string
|
||||
variables: PageVariables
|
||||
config?: ShopifyConfig
|
||||
preview?: boolean
|
||||
}): Promise<GetPageResult> {
|
||||
config = getConfig(config)
|
||||
return {}
|
||||
}
|
||||
|
||||
export default getPage
|
58
framework/swell/api/utils/create-api-handler.ts
Normal file
58
framework/swell/api/utils/create-api-handler.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
41
framework/swell/api/utils/fetch-all-products.ts
Normal file
41
framework/swell/api/utils/fetch-all-products.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { ProductEdge } from '../../schema'
|
||||
import { ShopifyConfig } from '..'
|
||||
|
||||
const fetchAllProducts = async ({
|
||||
config,
|
||||
query,
|
||||
variables,
|
||||
acc = [],
|
||||
cursor,
|
||||
}: {
|
||||
config: ShopifyConfig
|
||||
query: string
|
||||
acc?: ProductEdge[]
|
||||
variables?: any
|
||||
cursor?: string
|
||||
}): Promise<ProductEdge[]> => {
|
||||
const { data } = await config.fetch(query, {
|
||||
variables: { ...variables, cursor },
|
||||
})
|
||||
|
||||
const edges: ProductEdge[] = data.products?.edges ?? []
|
||||
const hasNextPage = data.products?.pageInfo?.hasNextPage
|
||||
acc = acc.concat(edges)
|
||||
|
||||
if (hasNextPage) {
|
||||
const cursor = edges.pop()?.cursor
|
||||
if (cursor) {
|
||||
return fetchAllProducts({
|
||||
config,
|
||||
query,
|
||||
variables,
|
||||
acc,
|
||||
cursor,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return acc
|
||||
}
|
||||
|
||||
export default fetchAllProducts
|
34
framework/swell/api/utils/fetch-graphql-api.ts
Normal file
34
framework/swell/api/utils/fetch-graphql-api.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { GraphQLFetcher } from '@commerce/api'
|
||||
import fetch from './fetch'
|
||||
|
||||
import { API_URL, API_TOKEN } from '../../const'
|
||||
import { getError } from '../../utils/handle-fetch-response'
|
||||
|
||||
const fetchGraphqlApi: GraphQLFetcher = async (
|
||||
query: string,
|
||||
{ variables } = {},
|
||||
fetchOptions
|
||||
) => {
|
||||
const res = await fetch(API_URL, {
|
||||
...fetchOptions,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Shopify-Storefront-Access-Token': API_TOKEN!,
|
||||
...fetchOptions?.headers,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
variables,
|
||||
}),
|
||||
})
|
||||
|
||||
const { data, errors, status } = await res.json()
|
||||
|
||||
if (errors) {
|
||||
throw getError(errors, status)
|
||||
}
|
||||
|
||||
return { data, res }
|
||||
}
|
||||
export default fetchGraphqlApi
|
2
framework/swell/api/utils/fetch.ts
Normal file
2
framework/swell/api/utils/fetch.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import zeitFetch from '@vercel/fetch'
|
||||
export default zeitFetch()
|
28
framework/swell/api/utils/is-allowed-method.ts
Normal file
28
framework/swell/api/utils/is-allowed-method.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
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
|
||||
}
|
2
framework/swell/api/wishlist/index.tsx
Normal file
2
framework/swell/api/wishlist/index.tsx
Normal file
@@ -0,0 +1,2 @@
|
||||
export type WishlistItem = { product: any; id: number }
|
||||
export default function () {}
|
Reference in New Issue
Block a user