Start updates

This commit is contained in:
cond0r 2021-05-24 08:42:47 +03:00
parent 03ad9ba902
commit 4d8e36e936
30 changed files with 183 additions and 226 deletions

View File

@ -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

View File

@ -1 +0,0 @@
export default function () {}

View File

@ -1 +0,0 @@
export default function () {}

View File

@ -1 +0,0 @@
export default function () {}

View File

@ -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, {}, {})

View File

@ -1 +0,0 @@
export default function () {}

View File

@ -1 +0,0 @@
export default function () {}

View File

@ -1 +0,0 @@
export default function () {}

View File

@ -1 +0,0 @@
export default function () {}

View File

@ -1 +0,0 @@
export default function () {}

View File

@ -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

View 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
}

View File

@ -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)
}
}
}

View File

@ -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
}

View File

@ -1,2 +0,0 @@
export type WishlistItem = { product: any; id: number }
export default function () {}

View File

@ -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'>
}

View File

@ -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.
"""

View File

@ -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

View 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']

View File

@ -0,0 +1,5 @@
import * as Core from '@commerce/types/customer'
export * from '@commerce/types/customer'
export type CustomerSchema = Core.CustomerSchema

View File

@ -0,0 +1,2 @@
export * from '@commerce/types'
export * from './cart'

View 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
}

View File

@ -0,0 +1 @@
export * from '@commerce/types/logout'

View 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>

View File

@ -0,0 +1 @@
export * from '@commerce/types/product'

View File

@ -0,0 +1 @@
export * from '@commerce/types/signup'

View File

@ -0,0 +1 @@
export * from '@commerce/types/site'

View File

@ -0,0 +1,8 @@
const getSiteInfoQuery = /* GraphQL */ `
query getSiteInfo {
shop {
name
}
}
`
export default getSiteInfoQuery

View File

@ -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 {

View File

@ -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"],