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 } = {}, { variables, preview } = {},
fetchOptions fetchOptions
) => { ) => {
// log.warn(query)
const config = getConfig() const config = getConfig()
const res = await fetch(config.commerceUrl, { const res = await fetch(config.commerceUrl, {
...fetchOptions, ...fetchOptions,

View File

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

View File

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

View File

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

View File

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

View File

@ -34,7 +34,7 @@ export function extendHook(
swrOptions?: SwrOptions<any | null> swrOptions?: SwrOptions<any | null>
) { ) {
const useCart = () => { const useCart = () => {
const response = useData<CartResult>({ query: getCartQuery }, [], customFetcher, swrOptions) const response = useData<Cart>({ query: getCartQuery }, [], customFetcher, swrOptions)
const res = useResponse(response, { const res = useResponse(response, {
normalizer: (data => { normalizer: (data => {
const order = data?.activeOrder || data?.addItemToOrder || data?.adjustOrderLine || data?.removeOrderLine; 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 { HookFetcher } from '@commerce/utils/types'
import type { SwrOptions } from '@commerce/utils/use-data' import type { SwrOptions } from '@commerce/utils/use-data'
import useCommerceCustomer from '@commerce/use-customer' 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 = { export const activeCustomerQuery = /* GraphQL */ `
url: '/api/bigcommerce/customers', query activeCustomer {
method: 'GET', activeCustomer {
id
firstName
lastName
emailAddress
} }
}
`;
export type { Customer } export const fetcher: HookFetcher<ActiveCustomerQuery> = async (
export const fetcher: HookFetcher<Customer | null> = async (
options, options,
_, _,
fetch fetch
) => { ) => {
// const data = await fetch<CustomerData | null>({ ...defaultOpts, ...options }) return await fetch<ActiveCustomerQuery>({ query: activeCustomerQuery })
// return data?.customer ?? null
return null;
} }
export function extendHook( export function extendHook(
customFetcher: typeof fetcher, customFetcher: typeof fetcher,
swrOptions?: SwrOptions<Customer | null> swrOptions?: SwrOptions<ActiveCustomerQuery>
) { ) {
const useCustomer = () => { const useCustomer = () => {
return useCommerceCustomer(defaultOpts, [], customFetcher, { const response = useCommerceCustomer({}, [], customFetcher, {
revalidateOnFocus: false, revalidateOnFocus: false,
...swrOptions, ...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'>) | ({ __typename?: 'SinglePrice' } & Pick<SinglePrice, 'value'>)
} }
export type LoginMutationVariables = Exact<{ export type LoginServerMutationVariables = Exact<{
email: Scalars['String'] email: Scalars['String']
password: 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' } & { export type LoginMutation = { __typename?: 'Mutation' } & {
login: login:
| ({ __typename?: 'CurrentUser' } & Pick<CurrentUser, 'id'>) | ({ __typename: 'CurrentUser' } & Pick<CurrentUser, 'id'>)
| { __typename?: 'InvalidCredentialsError' } | { __typename: 'InvalidCredentialsError' }
| { __typename?: 'NotVerifiedError' } | { __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' } | { __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<{ export type GetAllProductPathsQueryVariables = Exact<{
first?: Maybe<Scalars['Int']> first?: Maybe<Scalars['Int']>
}> }>