setup custom fetcher and auth hooks

This commit is contained in:
Greg Hoskin
2021-03-27 15:54:32 -06:00
parent 753234dc51
commit ee1d8ed461
37 changed files with 299 additions and 103 deletions

View File

@@ -4,7 +4,7 @@
const merge = require('deepmerge')
const PROVIDERS = ['bigcommerce', 'shopify']
const PROVIDERS = ['bigcommerce', 'shopify', 'swell']
function getProviderName() {
return process.env.BIGCOMMERCE_STOREFRONT_API_URL ? 'bigcommerce' : null

View File

@@ -1,2 +1,5 @@
SHOPIFY_STORE_DOMAIN=
SHOPIFY_STOREFRONT_ACCESS_TOKEN=
NEXT_PUBLIC_SWELL_STORE_ID=
NEXT_PUBLIC_SWELL_PUBLIC_KEY=

View File

@@ -22,23 +22,23 @@ if (!API_TOKEN) {
import fetchGraphqlApi from './utils/fetch-graphql-api'
export interface ShopifyConfig extends CommerceAPIConfig {}
export interface SwellConfig extends CommerceAPIConfig {}
export class Config {
private config: ShopifyConfig
private config: SwellConfig
constructor(config: ShopifyConfig) {
constructor(config: SwellConfig) {
this.config = config
}
getConfig(userConfig: Partial<ShopifyConfig> = {}) {
return Object.entries(userConfig).reduce<ShopifyConfig>(
getConfig(userConfig: Partial<SwellConfig> = {}) {
return Object.entries(userConfig).reduce<SwellConfig>(
(cfg, [key, value]) => Object.assign(cfg, { [key]: value }),
{ ...this.config }
)
}
setConfig(newConfig: Partial<ShopifyConfig>) {
setConfig(newConfig: Partial<SwellConfig>) {
Object.assign(this.config, newConfig)
}
}
@@ -53,10 +53,10 @@ const config = new Config({
customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE,
})
export function getConfig(userConfig?: Partial<ShopifyConfig>) {
export function getConfig(userConfig?: Partial<SwellConfig>) {
return config.getConfig(userConfig)
}
export function setConfig(newConfig: Partial<ShopifyConfig>) {
export function setConfig(newConfig: Partial<SwellConfig>) {
return config.setConfig(newConfig)
}

View File

@@ -1,8 +1,8 @@
import Client from 'shopify-buy'
import { ShopifyConfig } from '../index'
import { SwellConfig } from '../index'
type Options = {
config: ShopifyConfig
config: SwellConfig
}
const getAllCollections = async (options: Options) => {

View File

@@ -1,5 +1,5 @@
import { Page } from '../../schema'
import { ShopifyConfig, getConfig } from '..'
import { SwellConfig, getConfig } from '..'
export type GetPageResult<T extends { page?: any } = { page?: Page }> = T
@@ -15,7 +15,7 @@ async function getPage({
}: {
url?: string
variables: PageVariables
config?: ShopifyConfig
config?: SwellConfig
preview?: boolean
}): Promise<GetPageResult> {
config = getConfig(config)

View File

@@ -1,5 +1,5 @@
import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
import { ShopifyConfig, getConfig } from '..'
import { SwellConfig, getConfig } from '..'
export type ShopifyApiHandler<
T = any,
@@ -8,7 +8,7 @@ export type ShopifyApiHandler<
> = (
req: NextApiRequest,
res: NextApiResponse<ShopifyApiResponse<T>>,
config: ShopifyConfig,
config: SwellConfig,
handlers: H,
// Custom configs that may be used by a particular handler
options: Options
@@ -17,7 +17,7 @@ export type ShopifyApiHandler<
export type ShopifyHandler<T = any, Body = null> = (options: {
req: NextApiRequest
res: NextApiResponse<ShopifyApiResponse<T>>
config: ShopifyConfig
config: SwellConfig
body: Body
}) => void | Promise<void>
@@ -44,7 +44,7 @@ export default function createApiHandler<
operations,
options,
}: {
config?: ShopifyConfig
config?: SwellConfig
operations?: Partial<H>
options?: Options extends {} ? Partial<Options> : never
} = {}): NextApiHandler {

View File

@@ -1,5 +1,5 @@
import { ProductEdge } from '../../schema'
import { ShopifyConfig } from '..'
import { SwellConfig } from '..'
const fetchAllProducts = async ({
config,
@@ -8,7 +8,7 @@ const fetchAllProducts = async ({
acc = [],
cursor,
}: {
config: ShopifyConfig
config: SwellConfig
query: string
acc?: ProductEdge[]
variables?: any

View File

@@ -25,7 +25,8 @@ const getErrorMessage = ({ code, message }: CustomerUserError) => {
export const handler: MutationHook<null, {}, CustomerAccessTokenCreateInput> = {
fetchOptions: {
query: createCustomerAccessTokenMutation,
query: 'account',
method: 'login',
},
async fetcher({ input: { email, password }, options, fetch }) {
if (!(email && password)) {
@@ -40,9 +41,7 @@ export const handler: MutationHook<null, {}, CustomerAccessTokenCreateInput> = {
MutationCheckoutCreateArgs
>({
...options,
variables: {
input: { email, password },
},
variables: [email, password],
})
const errors = customerAccessTokenCreate?.customerUserErrors

View File

@@ -9,7 +9,8 @@ export default useLogout as UseLogout<typeof handler>
export const handler: MutationHook<null> = {
fetchOptions: {
query: customerAccessTokenDeleteMutation,
query: 'account',
method: 'logout',
},
async fetcher({ options, fetch }) {
await fetch({

View File

@@ -20,7 +20,8 @@ export const handler: MutationHook<
CustomerCreateInput
> = {
fetchOptions: {
query: customerCreateMutation,
query: 'account',
method: 'create',
},
async fetcher({
input: { firstName, lastName, email, password },
@@ -36,23 +37,20 @@ export const handler: MutationHook<
const data = await fetch({
...options,
variables: {
input: {
firstName,
lastName,
email,
password,
},
first_name: firstName,
last_name: lastName,
email,
password,
},
})
try {
const loginData = await fetch({
query: customerAccessTokenCreateMutation,
query: 'account',
method: 'login',
variables: {
input: {
email,
password,
},
email,
password,
},
})
handleLogin(loginData)

View File

@@ -18,7 +18,8 @@ export const handler: SWRHook<
{ isEmpty?: boolean }
> = {
fetchOptions: {
query: getCheckoutQuery,
query: 'cart',
method: 'get',
},
async fetcher({ input: { cartId: checkoutId }, options, fetch }) {
let checkout

View File

@@ -1,5 +1,5 @@
{
"provider": "shopify",
"provider": "swell",
"features": {
"wishlist": false
}

View File

@@ -1,4 +1,4 @@
import { getConfig, ShopifyConfig } from '../api'
import { getConfig, SwellConfig } from '../api'
import { PageEdge } from '../schema'
import { getAllPagesQuery } from '../utils/queries'
@@ -20,7 +20,7 @@ export type Page = {
const getAllPages = async (options?: {
variables?: Variables
config: ShopifyConfig
config: SwellConfig
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables = { first: 250 } } = options ?? {}

View File

@@ -1,4 +1,4 @@
import { getConfig, ShopifyConfig } from '../api'
import { getConfig, SwellConfig } from '../api'
import getPageQuery from '../utils/queries/get-page-query'
import { Page } from './get-all-pages'
@@ -10,7 +10,7 @@ export type GetPageResult<T extends { page?: any } = { page?: Page }> = T
const getPage = async (options: {
variables: Variables
config: ShopifyConfig
config: SwellConfig
preview?: boolean
}): Promise<GetPageResult> => {
let { config, variables } = options ?? {}

View File

@@ -1,7 +1,7 @@
import getCategories, { Category } from '../utils/get-categories'
import getVendors, { Brands } from '../utils/get-vendors'
import { getConfig, ShopifyConfig } from '../api'
import { getConfig, SwellConfig } from '../api'
export type GetSiteInfoResult<
T extends { categories: any[]; brands: any[] } = {
@@ -12,7 +12,7 @@ export type GetSiteInfoResult<
const getSiteInfo = async (options?: {
variables?: any
config: ShopifyConfig
config: SwellConfig
preview?: boolean
}): Promise<GetSiteInfoResult> => {
let { config } = options ?? {}

View File

@@ -11,3 +11,7 @@ export const SHOPIFY_COOKIE_EXPIRE = 30
export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json`
export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN
export const SWELL_STORE_ID = process.env.NEXT_PUBLIC_SWELL_STORE_ID
export const SWELL_PUBLIC_KEY = process.env.NEXT_PUBLIC_SWELL_PUBLIC_KEY

View File

@@ -1,4 +1,4 @@
import { getConfig, ShopifyConfig } from '../api'
import { getConfig, SwellConfig } from '../api'
import getCustomerIdQuery from '../utils/queries/get-customer-id-query'
import Cookies from 'js-cookie'
@@ -7,7 +7,7 @@ async function getCustomerId({
config,
}: {
customerToken: string
config?: ShopifyConfig
config?: SwellConfig
}): Promise<number | undefined> {
config = getConfig(config)

View File

@@ -1,20 +1,25 @@
import useCustomer, { UseCustomer } from '@commerce/customer/use-customer'
import { Customer } from '@commerce/types'
import { SWRHook } from '@commerce/utils/types'
import { getCustomerQuery, getCustomerToken } from '../utils'
import { normalizeCustomer } from '../utils/normalize'
// import { getCustomerQuery, getCustomerToken } from '../utils'
export default useCustomer as UseCustomer<typeof handler>
export const handler: SWRHook<Customer | null> = {
fetchOptions: {
query: getCustomerQuery,
query: 'account',
method: 'get',
},
async fetcher({ options, fetch }) {
// console.log('STORE_ID', STORE_ID, 'PUBLIC_KEY', PUBLIC_KEY);
// const data = await swell.account.get()
const data = await fetch<any | null>({
...options,
variables: { customerAccessToken: getCustomerToken() },
// variables: { customerAccessToken: getCustomerToken() },
})
return data.customer ?? null
console.log(`Customer data ${data}`)
return data ? normalizeCustomer(data) : null
},
useHook: ({ useData }) => (input) => {
return useData({
@@ -25,3 +30,26 @@ export const handler: SWRHook<Customer | null> = {
})
},
}
// const handler = (): { data: Customer } => {
// const swell = getContext();
// const response = swell.account.get();
// const { firstName, lastName, email, company, customerGroupId, notes, phone,
// entityId, addressCount, attributeCount, storeCredit } = response;
// return {
// data: {
// firstName,
// lastName,
// email,
// company,
// customerGroupId,
// notes,
// phone,
// entityId,
// addressCount,
// attributeCount,
// storeCredit
// }
// }
// }
// export default handler;

View File

@@ -1,18 +1,25 @@
import { Fetcher } from '@commerce/utils/types'
import { API_TOKEN, API_URL } from './const'
import { handleFetchResponse } from './utils'
import { swellConfig } from './index'
const fetcher: Fetcher = async ({ method = 'POST', variables, query }) => {
return handleFetchResponse(
await fetch(API_URL, {
method,
body: JSON.stringify({ query, variables }),
headers: {
'X-Shopify-Storefront-Access-Token': API_TOKEN!,
'Content-Type': 'application/json',
},
})
)
const fetcher: Fetcher = async ({ method = 'get', variables, query }) => {
const { swell } = swellConfig
async function callSwell() {
if (Array.isArray(variables)) {
const arg1 = variables[0]
const arg2 = variables[1]
const response = await swell[query][method](arg1, arg2)
console.log(response)
return handleFetchResponse(response)
} else {
const response = await swell[query][method](variables)
console.log(response)
return handleFetchResponse(response)
}
}
if (query) {
return await callSwell()
}
}
export default fetcher

View File

@@ -1,4 +1,5 @@
import * as React from 'react'
import swell from 'swell-js'
import { ReactNode } from 'react'
import {
@@ -7,30 +8,36 @@ import {
useCommerce as useCoreCommerce,
} from '@commerce'
import { shopifyProvider, ShopifyProvider } from './provider'
import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const'
import { swellProvider, SwellProvider } from './provider'
import {
SHOPIFY_CHECKOUT_ID_COOKIE,
SWELL_STORE_ID,
SWELL_PUBLIC_KEY,
} from './const'
swell.init(SWELL_STORE_ID, SWELL_PUBLIC_KEY)
export { shopifyProvider }
export type { ShopifyProvider }
export { swellProvider }
export type { SwellProvider }
export const shopifyConfig: CommerceConfig = {
export const swellConfig: any = {
locale: 'en-us',
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
swell,
}
export type ShopifyConfig = Partial<CommerceConfig>
export type SwellConfig = Partial<CommerceConfig>
export type ShopifyProps = {
export type SwellProps = {
children?: ReactNode
locale: string
} & ShopifyConfig
} & SwellConfig
export function CommerceProvider({ children, ...config }: ShopifyProps) {
export function CommerceProvider({ children, ...config }: SwellProps) {
return (
<CoreCommerceProvider
// TODO: Fix this type
provider={shopifyProvider as any}
config={{ ...shopifyConfig, ...config }}
provider={swellProvider as any}
config={{ ...swellConfig, ...config }}
>
{children}
</CoreCommerceProvider>

View File

@@ -1,10 +1,10 @@
import { CollectionEdge } from '../schema'
import { getConfig, ShopifyConfig } from '../api'
import { getConfig, SwellConfig } from '../api'
import getAllCollectionsQuery from '../utils/queries/get-all-collections-query'
const getAllCollections = async (options?: {
variables?: any
config: ShopifyConfig
config: SwellConfig
preview?: boolean
}) => {
let { config, variables = { first: 250 } } = options ?? {}

View File

@@ -1,5 +1,5 @@
import { Product } from '@commerce/types'
import { getConfig, ShopifyConfig } from '../api'
import { getConfig, SwellConfig } from '../api'
import fetchAllProducts from '../api/utils/fetch-all-products'
import { ProductEdge } from '../schema'
import getAllProductsPathsQuery from '../utils/queries/get-all-products-paths-query'
@@ -18,7 +18,7 @@ type ReturnType = {
const getAllProductPaths = async (options?: {
variables?: any
config?: ShopifyConfig
config?: SwellConfig
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables = { first: 250 } } = options ?? {}

View File

@@ -1,5 +1,5 @@
import { GraphQLFetcherResult } from '@commerce/api'
import { getConfig, ShopifyConfig } from '../api'
import { getConfig, SwellConfig } from '../api'
import { ProductEdge } from '../schema'
import { getAllProductsQuery } from '../utils/queries'
import { normalizeProduct } from '../utils/normalize'
@@ -16,7 +16,7 @@ type ReturnType = {
const getAllProducts = async (options: {
variables?: Variables
config?: ShopifyConfig
config?: SwellConfig
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables = { first: 250 } } = options ?? {}

View File

@@ -1,5 +1,5 @@
import { GraphQLFetcherResult } from '@commerce/api'
import { getConfig, ShopifyConfig } from '../api'
import { getConfig, SwellConfig } from '../api'
import { normalizeProduct, getProductQuery } from '../utils'
type Variables = {
@@ -12,7 +12,7 @@ type ReturnType = {
const getProduct = async (options: {
variables: Variables
config: ShopifyConfig
config: SwellConfig
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables } = options ?? {}

View File

@@ -14,7 +14,7 @@ import { handler as useSignup } from './auth/use-signup'
import fetcher from './fetcher'
export const shopifyProvider = {
export const swellProvider = {
locale: 'en-us',
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
storeDomain: STORE_DOMAIN,
@@ -28,4 +28,4 @@ export const shopifyProvider = {
},
}
export type ShopifyProvider = typeof shopifyProvider
export type SwellProvider = typeof swellProvider

1
framework/swell/swell-js.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
declare module 'swell-js'

View File

@@ -1,6 +1,11 @@
import * as Core from '@commerce/types'
import { CheckoutLineItem } from './schema'
export interface SwellCustomer extends Core.Customer {
first_name: string
last_name: string
}
export type ShopifyCheckout = {
id: string
webUrl: string

View File

@@ -1,4 +1,4 @@
import { ShopifyConfig } from '../api'
import { SwellConfig } from '../api'
import { CollectionEdge } from '../schema'
import getSiteCollectionsQuery from './queries/get-all-collections-query'
@@ -8,7 +8,7 @@ export type Category = {
path: string
}
const getCategories = async (config: ShopifyConfig): Promise<Category[]> => {
const getCategories = async (config: SwellConfig): Promise<Category[]> => {
const { data } = await config.fetch(getSiteCollectionsQuery, {
variables: {
first: 250,

View File

@@ -1,4 +1,4 @@
import { ShopifyConfig } from '../api'
import { SwellConfig } from '../api'
import fetchAllProducts from '../api/utils/fetch-all-products'
import getAllProductVendors from './queries/get-all-product-vendors-query'
@@ -13,7 +13,7 @@ export type BrandEdge = {
export type Brands = BrandEdge[]
const getVendors = async (config: ShopifyConfig): Promise<BrandEdge[]> => {
const getVendors = async (config: SwellConfig): Promise<BrandEdge[]> => {
const vendors = await fetchAllProducts({
config,
query: getAllProductVendors,

View File

@@ -11,15 +11,16 @@ export async function getAsyncError(res: Response) {
}
const handleFetchResponse = async (res: Response) => {
if (res.ok) {
const { data, errors } = await res.json()
// if (res.ok) {
// const { data, errors } = await res.json()
if (errors && errors.length) {
throw getError(errors, res.status)
}
// if (errors && errors.length) {
// throw getError(errors, res.status)
// }
return data
}
// return data
// }
if (res) return res
throw await getAsyncError(res)
}

View File

@@ -1,4 +1,5 @@
import { Product } from '@commerce/types'
import { Customer } from '@commerce/types'
import {
Product as ShopifyProduct,
@@ -11,7 +12,7 @@ import {
ProductOption,
} from '../schema'
import type { Cart, LineItem } from '../types'
import type { Cart, LineItem, SwellCustomer } from '../types'
const money = ({ amount, currencyCode }: MoneyV2) => {
return {
@@ -121,6 +122,15 @@ export function normalizeCart(checkout: Checkout): Cart {
}
}
export function normalizeCustomer(customer: SwellCustomer): Customer {
const { first_name: firstName, last_name: lastName } = customer
return {
...customer,
firstName,
lastName,
}
}
function normalizeLineItem({
node: { id, title, variant, quantity },
}: CheckoutLineItemEdge): LineItem {