Implement auth/customer hooks

This commit is contained in:
Michael Bromley 2021-01-25 20:28:09 +01:00
parent ed763381f5
commit eb7286f8e3
8 changed files with 140 additions and 58 deletions

View File

@ -8,7 +8,6 @@ const fetchGraphqlApi: GraphQLFetcher = async (
{ variables, preview } = {},
fetchOptions
) => {
// log.warn(query)
const config = getConfig()
const res = await fetch(config.commerceUrl, {
...fetchOptions,

View File

@ -5,7 +5,7 @@ import concatHeader from '../api/utils/concat-cookie'
import { VendureConfig, getConfig } from '../api'
export const loginMutation = /* GraphQL */ `
mutation login($email: String!, $password: String!) {
mutation loginServer($email: String!, $password: String!) {
login(username: $email, password: $password) {
...on CurrentUser {
id

View File

@ -2,43 +2,50 @@ import { useCallback } from 'react'
import type { HookFetcher } from '@commerce/utils/types'
import { CommerceError } from '@commerce/utils/errors'
import useCommerceLogin from '@commerce/use-login'
import type { LoginBody } from '../api/customers/login'
import useCustomer from '../customer/use-customer'
import { LoginMutation, LoginMutationVariables } from '@framework/schema'
const defaultOpts = {
url: '/api/bigcommerce/customers/login',
method: 'POST',
}
export const loginMutation = /* GraphQL */ `
mutation login($username: String! $password: String!) {
login(username: $username, password: $password) {
__typename
... on CurrentUser {
id
}
}
}
`
export type LoginInput = LoginBody
export const fetcher: HookFetcher<null, LoginBody> = (
export const fetcher: HookFetcher<LoginMutation, LoginMutationVariables> = (
options,
{ email, password },
{ username, password },
fetch
) => {
if (!(email && password)) {
if (!(username && password)) {
throw new CommerceError({
message:
'A first name, last name, email and password are required to login',
'An email address and password are required to login'
})
}
return fetch({
...defaultOpts,
...options,
body: { email, password },
query: loginMutation,
variables: { username, password }
})
}
export function extendHook(customFetcher: typeof fetcher) {
const useLogin = () => {
const { revalidate } = useCustomer()
const fn = useCommerceLogin<null, LoginInput>(defaultOpts, customFetcher)
const fn = useCommerceLogin<LoginMutation, LoginMutationVariables>({}, customFetcher)
return useCallback(
async function login(input: LoginInput) {
const data = await fn(input)
async function login(input: { email: string; password: string; }) {
const data = await fn({ username: input.email, password: input.password })
if (data.login.__typename !== 'CurrentUser') {
throw new CommerceError({ message: 'The credentials are not valid' });
}
await revalidate()
return data
},

View File

@ -2,28 +2,32 @@ import { useCallback } from 'react'
import type { HookFetcher } from '@commerce/utils/types'
import useCommerceLogout from '@commerce/use-logout'
import useCustomer from '../customer/use-customer'
import { LogoutMutation } from '@framework/schema'
const defaultOpts = {
url: '/api/bigcommerce/customers/logout',
method: 'GET',
}
export const logoutMutation = /* GraphQL */`
mutation logout {
logout {
success
}
}
`;
export const fetcher: HookFetcher<null> = (options, _, fetch) => {
export const fetcher: HookFetcher<LogoutMutation> = (options, _, fetch) => {
return fetch({
...defaultOpts,
...options,
query: logoutMutation
})
}
export function extendHook(customFetcher: typeof fetcher) {
const useLogout = () => {
const { mutate } = useCustomer()
const fn = useCommerceLogout<null>(defaultOpts, customFetcher)
const fn = useCommerceLogout<LogoutMutation>({}, customFetcher)
return useCallback(
async function login() {
async function logout() {
const data = await fn(null)
await mutate(null, false)
await mutate(null as any, false)
return data
},
[fn]

View File

@ -2,22 +2,33 @@ 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 { SignupMutation, SignupMutationVariables } from '@framework/schema'
const defaultOpts = {
url: '/api/bigcommerce/customers/signup',
method: 'POST',
export const signupMutation = /* GraphQL */ `
mutation signup($input: RegisterCustomerInput!) {
registerCustomerAccount(input: $input) {
... on Success {
success
}
}
}
`;
export type SignupInput = {
email: string;
firstName: string;
lastName: string;
password: string;
}
export type SignupInput = SignupBody
export const fetcher: HookFetcher<null, SignupBody> = (
export const fetcher: HookFetcher<SignupMutation, SignupMutationVariables> = (
options,
{ firstName, lastName, email, password },
{ input },
fetch
) => {
if (!(firstName && lastName && email && password)) {
const { firstName, lastName, emailAddress, password } = input;
if (!(firstName && lastName && emailAddress && password)) {
throw new CommerceError({
message:
'A first name, last name, email and password are required to signup',
@ -25,20 +36,27 @@ export const fetcher: HookFetcher<null, SignupBody> = (
}
return fetch({
...defaultOpts,
...options,
body: { firstName, lastName, email, password },
query: signupMutation,
variables: { input },
})
}
export function extendHook(customFetcher: typeof fetcher) {
const useSignup = () => {
const { revalidate } = useCustomer()
const fn = useCommerceSignup<null, SignupInput>(defaultOpts, customFetcher)
const fn = useCommerceSignup<SignupMutation, SignupMutationVariables>({}, customFetcher)
return useCallback(
async function signup(input: SignupInput) {
const data = await fn(input)
const data = await fn({
input: {
firstName: input.firstName,
lastName: input.lastName,
emailAddress: input.email,
password: input.password,
}
})
await revalidate()
return data
},

View File

@ -34,7 +34,7 @@ export function extendHook(
swrOptions?: SwrOptions<any | null>
) {
const useCart = () => {
const response = useData<CartResult>({ query: getCartQuery }, [], customFetcher, swrOptions)
const response = useData<Cart>({ query: getCartQuery }, [], customFetcher, swrOptions)
const res = useResponse(response, {
normalizer: (data => {
const order = data?.activeOrder || data?.addItemToOrder || data?.adjustOrderLine || data?.removeOrderLine;

View File

@ -1,33 +1,46 @@
import type { HookFetcher } from '@commerce/utils/types'
import type { SwrOptions } from '@commerce/utils/use-data'
import useCommerceCustomer from '@commerce/use-customer'
import type { Customer, CustomerData } from '../api/customers'
import { ActiveCustomerQuery } from '@framework/schema'
import useResponse from '@commerce/utils/use-response'
const defaultOpts = {
url: '/api/bigcommerce/customers',
method: 'GET',
}
export const activeCustomerQuery = /* GraphQL */ `
query activeCustomer {
activeCustomer {
id
firstName
lastName
emailAddress
}
}
`;
export type { Customer }
export const fetcher: HookFetcher<Customer | null> = async (
export const fetcher: HookFetcher<ActiveCustomerQuery> = async (
options,
_,
fetch
) => {
// const data = await fetch<CustomerData | null>({ ...defaultOpts, ...options })
// return data?.customer ?? null
return null;
return await fetch<ActiveCustomerQuery>({ query: activeCustomerQuery })
}
export function extendHook(
customFetcher: typeof fetcher,
swrOptions?: SwrOptions<Customer | null>
swrOptions?: SwrOptions<ActiveCustomerQuery>
) {
const useCustomer = () => {
return useCommerceCustomer(defaultOpts, [], customFetcher, {
const response = useCommerceCustomer({}, [], customFetcher, {
revalidateOnFocus: false,
...swrOptions,
});
return useResponse(response, {
normalizer: data => {
return data?.activeCustomer ? ({
firstName: data?.activeCustomer?.firstName ?? '',
lastName: data?.activeCustomer?.lastName ?? '',
email: data?.activeCustomer?.emailAddress ?? '',
}) : null;
},
})
}

View File

@ -2836,16 +2836,46 @@ export type SearchResultFragment = { __typename?: 'SearchResult' } & Pick<
| ({ __typename?: 'SinglePrice' } & Pick<SinglePrice, 'value'>)
}
export type LoginMutationVariables = Exact<{
export type LoginServerMutationVariables = Exact<{
email: Scalars['String']
password: Scalars['String']
}>
export type LoginServerMutation = { __typename?: 'Mutation' } & {
login:
| ({ __typename?: 'CurrentUser' } & Pick<CurrentUser, 'id'>)
| { __typename?: 'InvalidCredentialsError' }
| { __typename?: 'NotVerifiedError' }
| { __typename?: 'NativeAuthStrategyError' }
}
export type LoginMutationVariables = Exact<{
username: Scalars['String']
password: Scalars['String']
}>
export type LoginMutation = { __typename?: 'Mutation' } & {
login:
| ({ __typename?: 'CurrentUser' } & Pick<CurrentUser, 'id'>)
| { __typename?: 'InvalidCredentialsError' }
| { __typename?: 'NotVerifiedError' }
| ({ __typename: 'CurrentUser' } & Pick<CurrentUser, 'id'>)
| { __typename: 'InvalidCredentialsError' }
| { __typename: 'NotVerifiedError' }
| { __typename: 'NativeAuthStrategyError' }
}
export type LogoutMutationVariables = Exact<{ [key: string]: never }>
export type LogoutMutation = { __typename?: 'Mutation' } & {
logout: { __typename?: 'Success' } & Pick<Success, 'success'>
}
export type SignupMutationVariables = Exact<{
input: RegisterCustomerInput
}>
export type SignupMutation = { __typename?: 'Mutation' } & {
registerCustomerAccount:
| ({ __typename?: 'Success' } & Pick<Success, 'success'>)
| { __typename?: 'MissingPasswordError' }
| { __typename?: 'NativeAuthStrategyError' }
}
@ -2915,6 +2945,17 @@ export type GetCollectionsQuery = { __typename?: 'Query' } & {
}
}
export type ActiveCustomerQueryVariables = Exact<{ [key: string]: never }>
export type ActiveCustomerQuery = { __typename?: 'Query' } & {
activeCustomer?: Maybe<
{ __typename?: 'Customer' } & Pick<
Customer,
'id' | 'firstName' | 'lastName' | 'emailAddress'
>
>
}
export type GetAllProductPathsQueryVariables = Exact<{
first?: Maybe<Scalars['Int']>
}>