mirror of
https://github.com/vercel/commerce.git
synced 2025-03-14 22:42:33 +00:00
start customer auth & signup
This commit is contained in:
parent
2a29218285
commit
57ebfe42f5
@ -8,7 +8,6 @@ const fetchGraphqlApi: GraphQLFetcher = async (
|
||||
{ variables, preview } = {},
|
||||
fetchOptions
|
||||
) => {
|
||||
// log.warn(query)
|
||||
const config = getConfig()
|
||||
const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), {
|
||||
...fetchOptions,
|
||||
|
@ -2,17 +2,15 @@ import { useCallback } from 'react'
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import useCommerceSignup from '@commerce/use-signup'
|
||||
import type { SignupBody } from '../api/customers/signup'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
import customerCreateMutation from '@framework/utils/mutations/customer-create'
|
||||
import { CustomerCreateInput } from '@framework/schema'
|
||||
|
||||
const defaultOpts = {
|
||||
url: '/api/bigcommerce/customers/signup',
|
||||
method: 'POST',
|
||||
query: customerCreateMutation,
|
||||
}
|
||||
|
||||
export type SignupInput = SignupBody
|
||||
|
||||
export const fetcher: HookFetcher<null, SignupBody> = (
|
||||
export const fetcher: HookFetcher<null, CustomerCreateInput> = (
|
||||
options,
|
||||
{ firstName, lastName, email, password },
|
||||
fetch
|
||||
@ -27,17 +25,20 @@ export const fetcher: HookFetcher<null, SignupBody> = (
|
||||
return fetch({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
body: { firstName, lastName, email, password },
|
||||
variables: { firstName, lastName, email, password },
|
||||
})
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useSignup = () => {
|
||||
const { revalidate } = useCustomer()
|
||||
const fn = useCommerceSignup<null, SignupInput>(defaultOpts, customFetcher)
|
||||
const fn = useCommerceSignup<null, CustomerCreateInput>(
|
||||
defaultOpts,
|
||||
customFetcher
|
||||
)
|
||||
|
||||
return useCallback(
|
||||
async function signup(input: SignupInput) {
|
||||
async function signup(input: CustomerCreateInput) {
|
||||
const data = await fn(input)
|
||||
await revalidate()
|
||||
return data
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { CommerceAPIConfig } from '@commerce/api'
|
||||
import { SHOPIFY_CHECKOUT_ID_COOKIE } from '@framework/const'
|
||||
import fetchGraphqlApi from '../utils/fetch-graphql-api'
|
||||
|
||||
export interface ShopifyConfig extends CommerceAPIConfig {}
|
||||
|
||||
// No I don't like this - will fix it later
|
||||
const API_URL = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN
|
||||
const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN
|
||||
|
||||
@ -38,9 +38,13 @@ export class Config {
|
||||
}
|
||||
}
|
||||
|
||||
const ONE_DAY = 60 * 60 * 24
|
||||
|
||||
const config = new Config({
|
||||
commerceUrl: API_URL,
|
||||
apiToken: API_TOKEN,
|
||||
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
|
||||
cartCookieMaxAge: ONE_DAY * 30,
|
||||
fetch: fetchGraphqlApi,
|
||||
customerCookie: 'SHOP_TOKEN',
|
||||
})
|
||||
|
@ -1,13 +1,55 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { HookFetcher } from '@commerce/utils/types'
|
||||
import { CommerceError } from '@commerce/utils/errors'
|
||||
import useCommerceLogin from '@commerce/use-login'
|
||||
import useCustomer from '../customer/use-customer'
|
||||
import createCustomerAccessTokenMutation from '../utils/mutations/customer-acces-token-create'
|
||||
import { CustomerAccessTokenCreateInput } from '@framework/schema'
|
||||
|
||||
export function emptyHook() {
|
||||
const useEmptyHook = async (options = {}) => {
|
||||
return useCallback(async function () {
|
||||
return Promise.resolve()
|
||||
}, [])
|
||||
}
|
||||
|
||||
return useEmptyHook
|
||||
const defaultOpts = {
|
||||
query: createCustomerAccessTokenMutation,
|
||||
}
|
||||
|
||||
export default emptyHook
|
||||
export const fetcher: HookFetcher<null, CustomerAccessTokenCreateInput> = (
|
||||
options,
|
||||
{ email, password },
|
||||
fetch
|
||||
) => {
|
||||
if (!(email && password)) {
|
||||
throw new CommerceError({
|
||||
message:
|
||||
'A first name, last name, email and password are required to login',
|
||||
})
|
||||
}
|
||||
|
||||
return fetch({
|
||||
...defaultOpts,
|
||||
...options,
|
||||
body: { email, password },
|
||||
})
|
||||
}
|
||||
|
||||
export function extendHook(customFetcher: typeof fetcher) {
|
||||
const useLogin = () => {
|
||||
const { revalidate } = useCustomer()
|
||||
const fn = useCommerceLogin<null, CustomerAccessTokenCreateInput>(
|
||||
defaultOpts,
|
||||
customFetcher
|
||||
)
|
||||
|
||||
return useCallback(
|
||||
async function login(input: CustomerAccessTokenCreateInput) {
|
||||
const data = await fn(input)
|
||||
await revalidate()
|
||||
return data
|
||||
},
|
||||
[fn]
|
||||
)
|
||||
}
|
||||
|
||||
useLogin.extend = extendHook
|
||||
|
||||
return useLogin
|
||||
}
|
||||
|
||||
export default extendHook(fetcher)
|
||||
|
@ -7,6 +7,7 @@ import getCheckoutQuery from '@framework/utils/queries/get-checkout-query'
|
||||
|
||||
import { Cart } from '@commerce/types'
|
||||
import { checkoutToCart, checkoutCreate } from './utils'
|
||||
import { getConfig } from '@framework/api'
|
||||
|
||||
const defaultOpts = {
|
||||
query: getCheckoutQuery,
|
||||
|
44
framework/shopify/config.ts
Normal file
44
framework/shopify/config.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { CommerceError, FetcherError } from '@commerce/utils/errors'
|
||||
import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const'
|
||||
import type { CommerceConfig } from '@commerce'
|
||||
|
||||
export type ShopifyConfig = {
|
||||
locale: string
|
||||
cartCookie: string
|
||||
storeDomain: string | undefined
|
||||
} & CommerceConfig
|
||||
|
||||
export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN
|
||||
export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json`
|
||||
export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN
|
||||
|
||||
const shopifyConfig: ShopifyConfig = {
|
||||
locale: 'en-us',
|
||||
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
|
||||
storeDomain: STORE_DOMAIN,
|
||||
async fetcher({ method = 'POST', variables, query }) {
|
||||
const res = await fetch(API_URL, {
|
||||
method,
|
||||
body: JSON.stringify({ query, variables }),
|
||||
headers: {
|
||||
'X-Shopify-Storefront-Access-Token': API_TOKEN!,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
const json = await res.json()
|
||||
|
||||
if (json.errors) {
|
||||
throw new FetcherError({
|
||||
errors: json.errors ?? [
|
||||
{ message: 'Failed to fetch Shopify Storefront API' },
|
||||
],
|
||||
status: res.status,
|
||||
})
|
||||
}
|
||||
|
||||
return { data: json.data, res }
|
||||
},
|
||||
}
|
||||
|
||||
export default shopifyConfig
|
@ -2,18 +2,17 @@ import type { HookFetcher } from '@commerce/utils/types'
|
||||
import type { SwrOptions } from '@commerce/utils/use-data'
|
||||
import useCommerceCustomer from '@commerce/use-customer'
|
||||
|
||||
const defaultOpts = {}
|
||||
|
||||
export type Customer = {
|
||||
entityId: number
|
||||
firstName: string
|
||||
lastName: string
|
||||
email: string
|
||||
const defaultOpts = {
|
||||
query: '/api/bigcommerce/customers',
|
||||
}
|
||||
export type CustomerData = {}
|
||||
|
||||
export const fetcher: HookFetcher<Customer | null> = async () => {
|
||||
return null
|
||||
export const fetcher: HookFetcher<Customer | null> = async (
|
||||
options,
|
||||
_,
|
||||
fetch
|
||||
) => {
|
||||
const data = await fetch<CustomerData | null>({ ...defaultOpts, ...options })
|
||||
return data?.customer ?? null
|
||||
}
|
||||
|
||||
export function extendHook(
|
||||
@ -21,7 +20,10 @@ export function extendHook(
|
||||
swrOptions?: SwrOptions<Customer | null>
|
||||
) {
|
||||
const useCustomer = () => {
|
||||
return { data: { firstName: null, lastName: null, email: null } }
|
||||
return useCommerceCustomer(defaultOpts, [], customFetcher, {
|
||||
revalidateOnFocus: false,
|
||||
...swrOptions,
|
||||
})
|
||||
}
|
||||
|
||||
useCustomer.extend = extendHook
|
||||
|
@ -2,64 +2,11 @@ import { ReactNode } from 'react'
|
||||
import * as React from 'react'
|
||||
|
||||
import {
|
||||
CommerceConfig,
|
||||
CommerceProvider as CoreCommerceProvider,
|
||||
useCommerce as useCoreCommerce,
|
||||
} from '@commerce'
|
||||
|
||||
import { CommerceError, FetcherError } from '@commerce/utils/errors'
|
||||
import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const'
|
||||
|
||||
async function getText(res: Response) {
|
||||
try {
|
||||
return (await res.text()) || res.statusText
|
||||
} catch (error) {
|
||||
return res.statusText
|
||||
}
|
||||
}
|
||||
|
||||
async function getError(res: Response) {
|
||||
if (res.headers.get('Content-Type')?.includes('application/json')) {
|
||||
const data = await res.json()
|
||||
|
||||
return new FetcherError({ errors: data.errors, status: res.status })
|
||||
}
|
||||
return new FetcherError({ message: await getText(res), status: res.status })
|
||||
}
|
||||
|
||||
export type ShopifyConfig = Partial<CommerceConfig>
|
||||
|
||||
export const shopifyConfig: ShopifyConfig = {
|
||||
locale: 'en-us',
|
||||
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
|
||||
async fetcher({ method = 'POST', variables, query }) {
|
||||
const res = await fetch(
|
||||
`https://${process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN}/api/2021-01/graphql.json`,
|
||||
{
|
||||
method,
|
||||
body: JSON.stringify({ query, variables }),
|
||||
headers: {
|
||||
'X-Shopify-Storefront-Access-Token':
|
||||
process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if (res.ok) {
|
||||
const { data, errors } = await res.json()
|
||||
|
||||
if (errors && errors.length) {
|
||||
throw new CommerceError({
|
||||
message: errors[0].message,
|
||||
})
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
throw await getError(res)
|
||||
},
|
||||
}
|
||||
import shopifyConfig, { ShopifyConfig } from './config'
|
||||
|
||||
export type ShopifyProps = {
|
||||
children?: ReactNode
|
||||
|
@ -1,20 +1,31 @@
|
||||
import type { GraphQLFetcher } from '@commerce/api'
|
||||
import { FetcherError } from '@commerce/utils/errors'
|
||||
import { getConfig } from '../api/index'
|
||||
import fetch from './fetch'
|
||||
|
||||
import { STORE_DOMAIN, API_URL, API_TOKEN } from '../config'
|
||||
|
||||
if (!STORE_DOMAIN) {
|
||||
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`
|
||||
)
|
||||
}
|
||||
|
||||
const fetchGraphqlApi: GraphQLFetcher = async (
|
||||
query: string,
|
||||
{ variables } = {},
|
||||
fetchOptions
|
||||
) => {
|
||||
const { commerceUrl, apiToken } = getConfig()
|
||||
|
||||
const res = await fetch(`https://${commerceUrl}/api/2021-01/graphql.json`, {
|
||||
const res = await fetch(API_URL, {
|
||||
...fetchOptions,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Shopify-Storefront-Access-Token': apiToken,
|
||||
'X-Shopify-Storefront-Access-Token': API_TOKEN!,
|
||||
...fetchOptions?.headers,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
|
@ -0,0 +1,16 @@
|
||||
const customerAccessTokenCreateMutation = /* GraphQL */ `
|
||||
mutation customerAccessTokenCreate($input: CustomerAccessTokenCreateInput!) {
|
||||
customerAccessTokenCreate(input: $input) {
|
||||
customerAccessToken {
|
||||
accessToken
|
||||
expiresAt
|
||||
}
|
||||
customerUserErrors {
|
||||
code
|
||||
field
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
export default customerAccessTokenCreateMutation
|
15
framework/shopify/utils/mutations/customer-create.ts
Normal file
15
framework/shopify/utils/mutations/customer-create.ts
Normal file
@ -0,0 +1,15 @@
|
||||
const customerCreateMutation = /* GraphQL */ `
|
||||
mutation customerCreate($input: CustomerCreateInput!) {
|
||||
customerCreate(input: $input) {
|
||||
customerUserErrors {
|
||||
code
|
||||
field
|
||||
message
|
||||
}
|
||||
customer {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
export default customerCreateMutation
|
@ -1,3 +1,4 @@
|
||||
export { default as createCustomerMutation } from './customer-create'
|
||||
export { default as checkoutCreateMutation } from './checkout-create'
|
||||
export { default as checkoutLineItemAddMutation } from './checkout-line-item-add'
|
||||
export { default as checkoutLineItemUpdateMutation } from './checkout-create'
|
||||
|
Loading…
x
Reference in New Issue
Block a user