feat: add Config and fetchApi

This commit is contained in:
Bolaji Ayodeji 2021-07-09 16:39:00 +01:00 committed by Alessandro Casazza
parent 840dd8fea8
commit d47908def5
No known key found for this signature in database
GPG Key ID: 3AF41B06C6495D3D
10 changed files with 133 additions and 26 deletions

View File

@ -1,6 +1,6 @@
# Commerce Layer Provider
⚠️ Please note that this provider is still a work in progress.
⚠️ This provider is still a work in progress.
Before getting started, you should do the following:

View File

@ -1,6 +1,7 @@
import type { CommerceAPI, CommerceAPIConfig } from '@commerce/api'
import type { RequestInit } from '@vercel/fetch'
import { getCommerceApi as commerceApi } from '@commerce/api'
import createFetcher from './utils/fetch-local'
import createFetcher from './utils/fetch-api'
import getAllPages from './operations/get-all-pages'
import getPage from './operations/get-page'
@ -10,14 +11,63 @@ import getAllProductPaths from './operations/get-all-product-paths'
import getAllProducts from './operations/get-all-products'
import getProduct from './operations/get-product'
export interface LocalConfig extends CommerceAPIConfig {}
const config: LocalConfig = {
commerceUrl: '',
apiToken: '',
import { getToken } from './utils/get-token'
export interface CommercelayerConfig extends CommerceAPIConfig {
apiClientId: string
apiFetch<T>(
query: string,
endpoint: string,
fetchOptions?: RequestInit,
user?: UserCredentials
): Promise<T>
}
export type UserCredentials = {
email: string
password: string
}
const CLIENT_ID = process.env.COMMERCELAYER_CLIENT_ID
const ENDPOINT = process.env.COMMERCELAYER_ENDPOINT
const MARKET_SCOPE = process.env.COMMERCELAYER_MARKET_SCOPE
if (!CLIENT_ID) {
throw new Error(
`The environment variable COMMERCELAYER_CLIENT_ID is missing and it's required to access your store`
)
}
if (!ENDPOINT) {
throw new Error(
`The environment variable COMMERCELAYER_ENDPOINT is missing and it's required to access your store`
)
}
if (!MARKET_SCOPE) {
throw new Error(
`The environment variable COMMERCELAYER_MARKET_SCOPE is missing and it's required to access your store`
)
}
export async function getAccessToken(user?: UserCredentials) {
const token = await getToken({
clientId: CLIENT_ID,
endpoint: ENDPOINT,
scope: MARKET_SCOPE,
user,
})
return token
}
export interface CommercelayerConfig extends CommerceAPIConfig {}
const config: CommercelayerConfig = {
commerceUrl: ENDPOINT,
apiClientId: CLIENT_ID,
cartCookie: '',
customerCookie: '',
cartCookieMaxAge: 2592000,
fetch: createFetcher(() => getCommerceApi().getConfig()),
apiFetch: createFetcher(() => getCommerceApi().getConfig()),
}
const operations = {
@ -33,10 +83,12 @@ const operations = {
export const provider = { config, operations }
export type Provider = typeof provider
export type LocalAPI<P extends Provider = Provider> = CommerceAPI<P | any>
export type CommercelayerAPI<P extends Provider = Provider> = CommerceAPI<
P | any
>
export function getCommerceApi<P extends Provider>(
customProvider: P = provider as any
): LocalAPI<P> {
): CommercelayerAPI<P> {
return commerceApi(customProvider as any)
}

View File

@ -1,6 +1,6 @@
export type Page = { url: string }
export type GetAllPagesResult = { pages: Page[] }
import type { LocalConfig } from '../index'
import type { CommercelayerConfig } from '../index'
export default function getAllPagesOperation() {
function getAllPages({
@ -8,7 +8,7 @@ export default function getAllPagesOperation() {
preview,
}: {
url?: string
config?: Partial<LocalConfig>
config?: Partial<CommercelayerConfig>
preview?: boolean
}): Promise<GetAllPagesResult> {
return Promise.resolve({

View File

@ -1,7 +1,7 @@
import { Product } from '@commerce/types/product'
import { GetAllProductsOperation } from '@commerce/types/product'
import type { OperationContext } from '@commerce/api/operations'
import type { LocalConfig, Provider } from '../index'
import type { CommercelayerConfig, Provider } from '../index'
import data from '../../data.json'
export default function getAllProductsOperation({
@ -14,7 +14,7 @@ export default function getAllProductsOperation({
}: {
query?: string
variables?: T['variables']
config?: Partial<LocalConfig>
config?: Partial<CommercelayerConfig>
preview?: boolean
} = {}): Promise<{ products: Product[] | any[] }> {
return {

View File

@ -1,4 +1,4 @@
import type { LocalConfig } from '../index'
import type { CommercelayerConfig } from '../index'
import { Product } from '@commerce/types/product'
import { GetProductOperation } from '@commerce/types/product'
import data from '../../data.json'
@ -14,7 +14,7 @@ export default function getProductOperation({
}: {
query?: string
variables?: T['variables']
config?: Partial<LocalConfig>
config?: Partial<CommercelayerConfig>
preview?: boolean
} = {}): Promise<Product | {} | any> {
return {

View File

@ -1,6 +1,6 @@
import { OperationContext } from '@commerce/api/operations'
import { Category } from '@commerce/types/site'
import { LocalConfig } from '../index'
import { CommercelayerConfig } from '../index'
export type GetSiteInfoResult<
T extends { categories: any[]; brands: any[] } = {
@ -17,7 +17,7 @@ export default function getSiteInfoOperation({}: OperationContext<any>) {
}: {
query?: string
variables?: any
config?: Partial<LocalConfig>
config?: Partial<CommercelayerConfig>
preview?: boolean
} = {}): Promise<GetSiteInfoResult> {
return Promise.resolve({

View File

@ -0,0 +1,11 @@
// Email must start with and contain an alphanumeric character, contain a @ character, and . character
export const validateEmail = (email: string) => {
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
}
// Passwords must be at least eight characters and must contain at least one uppercase letter, one lowercase letter, one number and one special character
export const validatePassword = (password: string) => {
const re = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
return re.test(String(password).toLowerCase());
}

View File

@ -1,22 +1,28 @@
import type { RequestInit } from '@vercel/fetch'
import { FetcherError } from '@commerce/utils/errors'
import type { GraphQLFetcher } from '@commerce/api'
import type { LocalConfig } from '../index'
import { CommercelayerConfig, getAccessToken, UserCredentials } from '../index'
import fetch from './fetch'
const fetchGraphqlApi: (getConfig: () => LocalConfig) => GraphQLFetcher =
(getConfig) =>
async (query: string, { variables, preview } = {}, fetchOptions) => {
const fetchApi = <T>(getConfig: () => CommercelayerConfig) =>
async (
query: string,
endpoint: string,
fetchOptions?: RequestInit,
user?: UserCredentials
) => {
const config = getConfig()
const res = await fetch(config.commerceUrl, {
const getToken = await getAccessToken(user)
const token = getToken.accessToken
const res = await fetch(config.commerceUrl + endpoint, {
...fetchOptions,
method: 'POST',
headers: {
...fetchOptions?.headers,
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
query,
variables,
query
}),
})
@ -31,4 +37,4 @@ const fetchGraphqlApi: (getConfig: () => LocalConfig) => GraphQLFetcher =
return { data: json.data, res }
}
export default fetchGraphqlApi
export default fetchApi

View File

@ -0,0 +1,36 @@
import Cookies from "js-cookie";
import { getSalesChannelToken } from "@commercelayer/js-auth";
type GetTokenObj = {
clientId?: string;
endpoint?: string;
scope?: string;
user?: any;
};
export async function getToken({
clientId,
endpoint,
scope = "market:all",
user
}: GetTokenObj) {
let token = "" as any;
const getCookieToken = Cookies.get("clAccessToken");
if (!getCookieToken && clientId && endpoint) {
const auth = await getSalesChannelToken(
{
clientId,
endpoint,
scope
},
user
);
token = auth?.accessToken;
Cookies.set("clAccessToken", auth?.accessToken as string, {
// @ts-ignore
expires: auth?.expires
});
return auth ? { accessToken: auth.accessToken, customerId: auth.data.owner_id, ...auth.data } : null
}
return getCookieToken || "";
}

View File

@ -21,6 +21,8 @@
"node": ">=14.x"
},
"dependencies": {
"@commercelayer/js-auth": "^2.0.6",
"@commercelayer/js-sdk": "^4.1.3",
"@chec/commerce.js": "^2.8.0",
"@react-spring/web": "^9.4.1",
"@spree/storefront-api-v2-sdk": "^5.1.1",