4
0
forked from crowetic/commerce

Use functions instead of a class

This commit is contained in:
Luis Alvarez 2020-09-30 21:08:25 -05:00
parent e242525476
commit 2971b4f802
7 changed files with 141 additions and 122 deletions

View File

@ -1,20 +1,6 @@
import { import { CommerceAPIConfig } from 'lib/commerce/api';
CommerceAPI, import { GetAllProductsQueryVariables } from '../schema';
CommerceAPIOptions, import fetchAPI from './utils/fetch-api';
CommerceAPIFetchOptions,
} from 'lib/commerce/api';
import { GetAllProductsQuery, GetAllProductsQueryVariables } from '../schema';
import { getAllProductsQuery } from './operations/get-all-products';
type RecursivePartial<T> = {
[P in keyof T]?: RecursivePartial<T[P]>;
};
export interface GetAllProductsResult<T> {
products: T extends GetAllProductsQuery
? T['site']['products']['edges']
: unknown;
}
export interface Images { export interface Images {
small?: ImageOptions; small?: ImageOptions;
@ -28,10 +14,6 @@ export interface ImageOptions {
height?: number; height?: number;
} }
export interface BigcommerceAPIOptions extends CommerceAPIOptions {
images?: Images;
}
export type ProductImageVariables = Pick< export type ProductImageVariables = Pick<
GetAllProductsQueryVariables, GetAllProductsQueryVariables,
| 'imgSmallWidth' | 'imgSmallWidth'
@ -44,83 +26,51 @@ export type ProductImageVariables = Pick<
| 'imgXLHeight' | 'imgXLHeight'
>; >;
export type ProductVariables = Images & export interface BigcommerceConfig extends CommerceAPIConfig {
Omit<GetAllProductsQueryVariables, keyof ProductImageVariables>; images?: Images;
readonly imageVariables?: ProductImageVariables;
export default class BigcommerceAPI implements CommerceAPI { }
commerceUrl: string;
apiToken: string; const API_URL = process.env.BIGCOMMERCE_STOREFRONT_API_URL;
imageVariables?: ProductImageVariables; const API_TOKEN = process.env.BIGCOMMERCE_STOREFRONT_API_TOKEN;
constructor({ commerceUrl, apiToken, images }: BigcommerceAPIOptions) { if (!API_URL) {
this.commerceUrl = commerceUrl; throw new Error(
this.apiToken = apiToken; `The environment variable BIGCOMMERCE_STOREFRONT_API_URL is missing and it's required to access your store`
this.imageVariables = { );
imgSmallWidth: images?.small?.width, }
imgSmallHeight: images?.small?.height,
imgMediumWidth: images?.medium?.height, if (!API_TOKEN) {
imgMediumHeight: images?.medium?.height, throw new Error(
imgLargeWidth: images?.large?.height, `The environment variable BIGCOMMERCE_STOREFRONT_API_TOKEN is missing and it's required to access your store`
imgLargeHeight: images?.large?.height, );
imgXLWidth: images?.xl?.height, }
imgXLHeight: images?.xl?.height,
}; const config: BigcommerceConfig = {
} commerceUrl: API_URL,
apiToken: API_TOKEN,
async fetch<Q, V = any>( fetch: fetchAPI,
query: string, get imageVariables() {
{ variables, preview }: CommerceAPIFetchOptions<V> = {} const { images } = this;
): Promise<Q> { return images
const res = await fetch(this.commerceUrl + (preview ? '/preview' : ''), { ? {
method: 'POST', imgSmallWidth: images.small?.width,
headers: { imgSmallHeight: images.small?.height,
'Content-Type': 'application/json', imgMediumWidth: images.medium?.height,
Authorization: `Bearer ${this.apiToken}`, imgMediumHeight: images.medium?.height,
}, imgLargeWidth: images.large?.height,
body: JSON.stringify({ imgLargeHeight: images.large?.height,
query, imgXLWidth: images.xl?.height,
variables, imgXLHeight: images.xl?.height,
}), }
}); : undefined;
},
const json = await res.json(); };
if (json.errors) {
console.error(json.errors); export function getConfig() {
throw new Error('Failed to fetch API'); return config;
} }
return json.data;
} export function setConfig(newConfig: Partial<BigcommerceConfig>) {
Object.assign(config, newConfig);
async getAllProducts<T, V = any>(opts: {
query: string;
variables?: V;
}): Promise<GetAllProductsResult<T>>;
async getAllProducts(opts?: {
query?: string;
variables?: ProductVariables;
}): Promise<GetAllProductsResult<GetAllProductsQuery>>;
async getAllProducts({
query = getAllProductsQuery,
variables: vars,
}: {
query?: string;
variables?: ProductVariables;
} = {}): Promise<
GetAllProductsResult<RecursivePartial<GetAllProductsQuery>>
> {
const variables: GetAllProductsQueryVariables = {
...this.imageVariables,
...vars,
};
const data = await this.fetch<RecursivePartial<GetAllProductsQuery>>(
query,
{ variables }
);
return {
products: data?.site?.products?.edges,
};
}
} }

View File

@ -1,3 +1,10 @@
import {
GetAllProductsQuery,
GetAllProductsQueryVariables,
} from 'lib/bigcommerce/schema';
import { getConfig, Images, ProductImageVariables } from '..';
import { RecursivePartial } from '../types';
export const getAllProductsQuery = /* GraphQL */ ` export const getAllProductsQuery = /* GraphQL */ `
query getAllProducts( query getAllProducts(
$first: Int = 10 $first: Int = 10
@ -94,3 +101,46 @@ export const getAllProductsQuery = /* GraphQL */ `
} }
} }
`; `;
export interface GetAllProductsResult<T> {
products: T extends GetAllProductsQuery
? T['site']['products']['edges']
: unknown;
}
export type ProductVariables = Images &
Omit<GetAllProductsQueryVariables, keyof ProductImageVariables>;
async function getAllProducts<T, V = any>(opts: {
query: string;
variables?: V;
}): Promise<GetAllProductsResult<T>>;
async function getAllProducts(opts?: {
query?: string;
variables?: ProductVariables;
}): Promise<GetAllProductsResult<GetAllProductsQuery>>;
async function getAllProducts({
query = getAllProductsQuery,
variables: vars,
}: {
query?: string;
variables?: ProductVariables;
} = {}): Promise<GetAllProductsResult<RecursivePartial<GetAllProductsQuery>>> {
const config = getConfig();
const variables: GetAllProductsQueryVariables = {
...config.imageVariables,
...vars,
};
const data = await config.fetch<RecursivePartial<GetAllProductsQuery>>(
query,
{ variables }
);
return {
products: data?.site?.products?.edges,
};
}
export default getAllProducts;

View File

@ -0,0 +1,3 @@
export type RecursivePartial<T> = {
[P in keyof T]?: RecursivePartial<T[P]>;
};

View File

@ -0,0 +1,27 @@
import { CommerceAPIFetchOptions } from 'lib/commerce/api';
import { getConfig } from '..';
export default async function fetchAPI<Q, V = any>(
query: string,
{ variables, preview }: CommerceAPIFetchOptions<V> = {}
): Promise<Q> {
const config = getConfig();
const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${config.apiToken}`,
},
body: JSON.stringify({
query,
variables,
}),
});
const json = await res.json();
if (json.errors) {
console.error(json.errors);
throw new Error('Failed to fetch API');
}
return json.data;
}

View File

@ -1,9 +0,0 @@
import BigcommerceAPI from './bigcommerce/api';
const API_URL = process.env.NEXT_EXAMPLE_BIGCOMMERCE_STOREFRONT_API_URL!;
const API_TOKEN = process.env.NEXT_EXAMPLE_BIGCOMMERCE_STOREFRONT_API_TOKEN!;
export const commerce = new BigcommerceAPI({
commerceUrl: API_URL,
apiToken: API_TOKEN,
});

View File

@ -1,6 +1,10 @@
export interface CommerceAPIOptions { export interface CommerceAPIConfig {
commerceUrl: string; commerceUrl: string;
apiToken: string; apiToken: string;
fetch<Q, V = any>(
query: string,
queryData?: CommerceAPIFetchOptions<V>
): Promise<Q>;
} }
export interface CommerceAPIFetchOptions<V> { export interface CommerceAPIFetchOptions<V> {
@ -8,14 +12,8 @@ export interface CommerceAPIFetchOptions<V> {
preview?: boolean; preview?: boolean;
} }
export interface CommerceAPI { // TODO: define interfaces for all the available operations
commerceUrl: string;
apiToken: string;
fetch<Q, V = any>( // export interface CommerceAPI {
query: string, // getAllProducts(options?: { query: string }): Promise<any>;
queryData?: CommerceAPIFetchOptions<V> // }
): Promise<Q>;
getAllProducts(options?: { query: string }): Promise<any>;
}

View File

@ -1,9 +1,9 @@
import { GetStaticPropsContext, InferGetStaticPropsType } from 'next'; import { GetStaticPropsContext, InferGetStaticPropsType } from 'next';
import { commerce } from 'lib/commerce-api'; import getAllProducts from 'lib/bigcommerce/api/operations/get-all-products';
import { Layout } from '@components/core'; import { Layout } from '@components/core';
export async function getStaticProps({ preview }: GetStaticPropsContext) { export async function getStaticProps({ preview }: GetStaticPropsContext) {
const { products } = await commerce.getAllProducts(); const { products } = await getAllProducts();
return { return {
props: { products }, props: { products },