diff --git a/framework/commerce/api/index.ts b/framework/commerce/api/index.ts index 6d4c16f2a..6a28a9597 100644 --- a/framework/commerce/api/index.ts +++ b/framework/commerce/api/index.ts @@ -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, @@ -130,6 +132,18 @@ export function getEndpoint< } } +export const createEndpoint = >( + endpoint: API['endpoint'] +) =>

( + commerce: CommerceAPI

, + context?: Partial & { + config?: P['config'] + options?: API['schema']['endpoint']['options'] + } +): NextApiHandler => { + return getEndpoint(commerce, { ...endpoint, ...context }) +} + export interface CommerceAPIConfig { locale?: string commerceUrl: string diff --git a/framework/shopify/api/cart/index.ts b/framework/shopify/api/cart/index.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/framework/shopify/api/cart/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/framework/shopify/api/catalog/index.ts b/framework/shopify/api/catalog/index.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/framework/shopify/api/catalog/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/framework/shopify/api/catalog/products.ts b/framework/shopify/api/catalog/products.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/framework/shopify/api/catalog/products.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/framework/shopify/api/checkout/index.ts b/framework/shopify/api/checkout/index.ts deleted file mode 100644 index 244078466..000000000 --- a/framework/shopify/api/checkout/index.ts +++ /dev/null @@ -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 = 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, {}, {}) diff --git a/framework/shopify/api/customer.ts b/framework/shopify/api/customer.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/framework/shopify/api/customer.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/framework/shopify/api/customers/index.ts b/framework/shopify/api/customers/index.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/framework/shopify/api/customers/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/framework/shopify/api/customers/login.ts b/framework/shopify/api/customers/login.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/framework/shopify/api/customers/login.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/framework/shopify/api/customers/logout.ts b/framework/shopify/api/customers/logout.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/framework/shopify/api/customers/logout.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/framework/shopify/api/customers/signup.ts b/framework/shopify/api/customers/signup.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/framework/shopify/api/customers/signup.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index 387ed02fc..3d49ed8b5 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -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 = {}) { - return Object.entries(userConfig).reduce( - (cfg, [key, value]) => Object.assign(cfg, { [key]: value }), - { ...this.config } - ) - } - - setConfig(newConfig: Partial) { - 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) { - 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) { - return config.setConfig(newConfig) +export const provider = { + config: config, + operations: { getSiteInfo }, } + +export type Provider = typeof provider + +export default provider diff --git a/framework/shopify/api/operations/get-site-info.ts b/framework/shopify/api/operations/get-site-info.ts new file mode 100644 index 000000000..4a9f41576 --- /dev/null +++ b/framework/shopify/api/operations/get-site-info.ts @@ -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) { + async function getSiteInfo({ + query = getSiteInfoQuery, + config, + }: { + query?: string + config?: ShopifyConfig + preview?: boolean + } = {}): Promise { + config = commerce.getConfig(config) + + const categories = await getCategories(config) + const brands = await getVendors(config) + + const { data } = await config.fetch(query) + + return { + categories, + brands, + } + } + + return getSiteInfo +} diff --git a/framework/shopify/api/utils/create-api-handler.ts b/framework/shopify/api/utils/create-api-handler.ts deleted file mode 100644 index 8820aeabc..000000000 --- a/framework/shopify/api/utils/create-api-handler.ts +++ /dev/null @@ -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>, - config: ShopifyConfig, - handlers: H, - // Custom configs that may be used by a particular handler - options: Options -) => void | Promise - -export type ShopifyHandler = (options: { - req: NextApiRequest - res: NextApiResponse> - config: ShopifyConfig - body: Body -}) => void | Promise - -export type ShopifyHandlers = { - [k: string]: ShopifyHandler -} - -export type ShopifyApiResponse = { - data: T | null - errors?: { message: string; code?: string }[] -} - -export default function createApiHandler< - T = any, - H extends ShopifyHandlers = {}, - Options extends {} = {} ->( - handler: ShopifyApiHandler, - handlers: H, - defaultOptions: Options -) { - return function getApiHandler({ - config, - operations, - options, - }: { - config?: ShopifyConfig - operations?: Partial - options?: Options extends {} ? Partial : never - } = {}): NextApiHandler { - const ops = { ...operations, ...handlers } - const opts = { ...defaultOptions, ...options } - - return function apiHandler(req, res) { - return handler(req, res, getConfig(config), ops, opts) - } - } -} diff --git a/framework/shopify/api/utils/is-allowed-method.ts b/framework/shopify/api/utils/is-allowed-method.ts deleted file mode 100644 index 78bbba568..000000000 --- a/framework/shopify/api/utils/is-allowed-method.ts +++ /dev/null @@ -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 -} diff --git a/framework/shopify/api/wishlist/index.tsx b/framework/shopify/api/wishlist/index.tsx deleted file mode 100644 index a72856673..000000000 --- a/framework/shopify/api/wishlist/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export type WishlistItem = { product: any; id: number } -export default function () {} diff --git a/framework/shopify/schema.d.ts b/framework/shopify/schema.d.ts index 317351b2e..328f0ff1b 100644 --- a/framework/shopify/schema.d.ts +++ b/framework/shopify/schema.d.ts @@ -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 +} diff --git a/framework/shopify/schema.graphql b/framework/shopify/schema.graphql index 7854508ef..9c657fe43 100644 --- a/framework/shopify/schema.graphql +++ b/framework/shopify/schema.graphql @@ -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. """ diff --git a/framework/shopify/types.ts b/framework/shopify/types.ts deleted file mode 100644 index ba0f13997..000000000 --- a/framework/shopify/types.ts +++ /dev/null @@ -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 - -export type AddCartItemHandlerBody = Core.AddCartItemHandlerBody - -export type UpdateCartItemBody = Core.UpdateCartItemBody - -export type UpdateCartItemHandlerBody = Core.UpdateCartItemHandlerBody - -export type RemoveCartItemBody = Core.RemoveCartItemBody - -export type RemoveCartItemHandlerBody = Core.RemoveCartItemHandlerBody diff --git a/framework/shopify/types/cart.ts b/framework/shopify/types/cart.ts new file mode 100644 index 000000000..d792243d8 --- /dev/null +++ b/framework/shopify/types/cart.ts @@ -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 + +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 + +export type CartHandlers = Core.CartHandlers + +export type GetCartHandler = CartHandlers['getCart'] +export type AddItemHandler = CartHandlers['addItem'] +export type UpdateItemHandler = CartHandlers['updateItem'] +export type RemoveItemHandler = CartHandlers['removeItem'] diff --git a/framework/shopify/types/customer.ts b/framework/shopify/types/customer.ts new file mode 100644 index 000000000..427bc0b03 --- /dev/null +++ b/framework/shopify/types/customer.ts @@ -0,0 +1,5 @@ +import * as Core from '@commerce/types/customer' + +export * from '@commerce/types/customer' + +export type CustomerSchema = Core.CustomerSchema diff --git a/framework/shopify/types/index.ts b/framework/shopify/types/index.ts new file mode 100644 index 000000000..a0792d798 --- /dev/null +++ b/framework/shopify/types/index.ts @@ -0,0 +1,2 @@ +export * from '@commerce/types' +export * from './cart' diff --git a/framework/shopify/types/login.ts b/framework/shopify/types/login.ts new file mode 100644 index 000000000..24d5077ff --- /dev/null +++ b/framework/shopify/types/login.ts @@ -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 +} diff --git a/framework/shopify/types/logout.ts b/framework/shopify/types/logout.ts new file mode 100644 index 000000000..9f0a466af --- /dev/null +++ b/framework/shopify/types/logout.ts @@ -0,0 +1 @@ +export * from '@commerce/types/logout' diff --git a/framework/shopify/types/page.ts b/framework/shopify/types/page.ts new file mode 100644 index 000000000..8f1adeb24 --- /dev/null +++ b/framework/shopify/types/page.ts @@ -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 +export type GetPageOperation = Core.GetPageOperation diff --git a/framework/shopify/types/product.ts b/framework/shopify/types/product.ts new file mode 100644 index 000000000..c776d58fa --- /dev/null +++ b/framework/shopify/types/product.ts @@ -0,0 +1 @@ +export * from '@commerce/types/product' diff --git a/framework/shopify/types/signup.ts b/framework/shopify/types/signup.ts new file mode 100644 index 000000000..58543c6f6 --- /dev/null +++ b/framework/shopify/types/signup.ts @@ -0,0 +1 @@ +export * from '@commerce/types/signup' diff --git a/framework/shopify/types/site.ts b/framework/shopify/types/site.ts new file mode 100644 index 000000000..bfef69cf9 --- /dev/null +++ b/framework/shopify/types/site.ts @@ -0,0 +1 @@ +export * from '@commerce/types/site' diff --git a/framework/shopify/utils/queries/get-site-info-query.ts b/framework/shopify/utils/queries/get-site-info-query.ts new file mode 100644 index 000000000..74215572a --- /dev/null +++ b/framework/shopify/utils/queries/get-site-info-query.ts @@ -0,0 +1,8 @@ +const getSiteInfoQuery = /* GraphQL */ ` + query getSiteInfo { + shop { + name + } + } +` +export default getSiteInfoQuery diff --git a/pages/index.tsx b/pages/index.tsx index d74a649fb..b1a8b2105 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -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 { diff --git a/tsconfig.json b/tsconfig.json index 9e712fb18..e20f37099 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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"],