Implement variants and options

This commit is contained in:
goncy 2021-08-10 10:51:53 -03:00
parent 04f9799636
commit 07bc16b9a7
6 changed files with 113 additions and 80 deletions

View File

@ -1,15 +1,33 @@
import data from '../../data.json' import type { OrdercloudConfig, Provider } from '../index'
import type { OperationContext } from '@commerce/api/operations'
import type { GetAllProductPathsOperation } from '@commerce/types/product'
import { RawProduct } from '@framework/types/product'
export type GetAllProductPathsResult = { export type GetAllProductPathsResult = {
products: Array<{ path: string }> products: Array<{ path: string }>
} }
export default function getAllProductPathsOperation() { export default function getAllProductPathsOperation({
function getAllProductPaths(): Promise<GetAllProductPathsResult> { commerce,
return Promise.resolve({ }: OperationContext<Provider>) {
products: [] async function getAllProductPaths<T extends GetAllProductPathsOperation>({
// products: data.products.map(({ path }) => ({ path })), config,
}) }: {
config?: Partial<OrdercloudConfig>
} = {}): Promise<T['data']> {
// Get fetch from the config
const { fetch } = commerce.getConfig(config)
// Get all products
const rawProducts: RawProduct[] = await fetch<{ Items: RawProduct[] }>(
'GET',
'/products'
).then((response) => response.Items)
return {
// Match a path for every product retrieved
products: rawProducts.map((product) => ({ path: `/${product.ID}` })),
}
} }
return getAllProductPaths return getAllProductPaths

View File

@ -1,4 +1,3 @@
import type { Product } from '@commerce/types/product'
import type { GetAllProductsOperation } from '@commerce/types/product' import type { GetAllProductsOperation } from '@commerce/types/product'
import type { OperationContext } from '@commerce/api/operations' import type { OperationContext } from '@commerce/api/operations'
import type { RawProduct } from '@framework/types/product' import type { RawProduct } from '@framework/types/product'
@ -16,15 +15,18 @@ export default function getAllProductsOperation({
variables?: T['variables'] variables?: T['variables']
config?: Partial<OrdercloudConfig> config?: Partial<OrdercloudConfig>
preview?: boolean preview?: boolean
} = {}): Promise<{ products: Product[] }> { } = {}): Promise<T['data']> {
// Get fetch from the config
const { fetch } = commerce.getConfig(config) const { fetch } = commerce.getConfig(config)
// Get all products
const rawProducts: RawProduct[] = await fetch<{ Items: RawProduct[] }>( const rawProducts: RawProduct[] = await fetch<{ Items: RawProduct[] }>(
'GET', 'GET',
'/products' '/products'
).then((response) => response.Items) ).then((response) => response.Items)
return { return {
// Normalize products to commerce schema
products: rawProducts.map(normalizeProduct), products: rawProducts.map(normalizeProduct),
} }
} }

View File

@ -1,6 +1,5 @@
import type { OperationContext } from '@commerce/api/operations' import type { OperationContext } from '@commerce/api/operations'
import type { RawProduct } from '@framework/types/product' import type { RawProduct } from '@framework/types/product'
import type { Product } from '@commerce/types/product'
import type { GetProductOperation } from '@commerce/types/product' import type { GetProductOperation } from '@commerce/types/product'
import type { OrdercloudConfig, Provider } from '../index' import type { OrdercloudConfig, Provider } from '../index'
@ -17,15 +16,18 @@ export default function getProductOperation({
variables?: T['variables'] variables?: T['variables']
config?: Partial<OrdercloudConfig> config?: Partial<OrdercloudConfig>
preview?: boolean preview?: boolean
} = {}): Promise<{ product: Product }> { } = {}): Promise<T['data']> {
// Get fetch from the config
const { fetch } = commerce.getConfig(config) const { fetch } = commerce.getConfig(config)
// Get a single product
const rawProduct: RawProduct = await fetch<RawProduct>( const rawProduct: RawProduct = await fetch<RawProduct>(
'GET', 'GET',
`/products/${variables?.slug}` `/products/${variables?.slug}`
) )
return { return {
// Normalize product to commerce schema
product: normalizeProduct(rawProduct), product: normalizeProduct(rawProduct),
} }
} }

View File

@ -1,5 +1,6 @@
import { FetcherError } from '@commerce/utils/errors'
import type { OrdercloudConfig } from '../index' import type { OrdercloudConfig } from '../index'
import { FetcherError } from '@commerce/utils/errors'
import fetch from './fetch' import fetch from './fetch'
const fetchRestApi: ( const fetchRestApi: (
@ -18,8 +19,8 @@ const fetchRestApi: (
fetchOptions?: Record<string, any> fetchOptions?: Record<string, any>
) => { ) => {
const { commerceUrl } = getConfig() const { commerceUrl } = getConfig()
// Check if we have a token stored
if (!global.token) { async function getToken() {
// If not, get a new one and store it // If not, get a new one and store it
const authResponse = await fetch(`${commerceUrl}/oauth/token`, { const authResponse = await fetch(`${commerceUrl}/oauth/token`, {
method: 'POST', method: 'POST',
@ -48,6 +49,7 @@ const fetchRestApi: (
.then((response) => response.access_token) .then((response) => response.access_token)
} }
async function fetchData(retries = 0): Promise<T> {
// Do the request with the correct headers // Do the request with the correct headers
const dataResponse = await fetch(`${commerceUrl}/v1${resource}`, { const dataResponse = await fetch(`${commerceUrl}/v1${resource}`, {
...fetchOptions, ...fetchOptions,
@ -62,6 +64,21 @@ const fetchRestApi: (
// If something failed getting the data response // If something failed getting the data response
if (!dataResponse.ok) { if (!dataResponse.ok) {
// If token is expired
if (dataResponse.status === 401) {
// Reset it
global.token = null
// Get a new one
await getToken()
// And if retries left
if (retries < 2) {
// Refetch
return fetchData(retries + 1)
}
}
// Get the body of it // Get the body of it
const error = await dataResponse.json() const error = await dataResponse.json()
@ -72,8 +89,18 @@ const fetchRestApi: (
}) })
} }
// Return data response
return dataResponse.json() as Promise<T>
}
// Check if we have a token stored
if (!global.token) {
// If not, get a new one and store it
await getToken()
}
// Return the data and specify the expected type // Return the data and specify the expected type
return (await dataResponse.json()) as T return fetchData()
} }
export default fetchRestApi export default fetchRestApi

View File

@ -23,5 +23,6 @@ export interface RawProduct {
Images: { Images: {
url: string url: string
}[] }[]
Facets: Record<string, string[]>
} }
} }

View File

@ -12,49 +12,32 @@ export function normalize(product: RawProduct): Product {
value: product.xp.Price, value: product.xp.Price,
currencyCode: product.xp.PriceCurrency, currencyCode: product.xp.PriceCurrency,
}, },
// TODO: Implement this // Variants are not always present, in case they are not, return a single unique variant
variants: [ variants:
product.VariantCount === 0
? [
{ {
id: 'unique', id: 'unique',
options: [ options: [
{ {
id: 'unique', id: 'unique',
displayName: 'Model', displayName: 'Unique',
values: [ values: [{ label: 'Unique' }],
{
label: 'Unique',
}, },
], ],
}, },
], ]
}, : [],
], // Facets are not always present, just iterate them if they are
options: [ options: product.xp.Facets
{ ? Object.entries(product.xp.Facets).map(([key, values]) => ({
id: 'option-color', id: key,
displayName: 'Color', displayName: key,
values: [ __typename: 'MultipleChoiceOption',
{ values: values.map((value) => ({
label: 'color', label: value,
hexColors: ['#222'], })),
}, }))
], : [],
},
{
id: 'option-size',
displayName: 'Size',
values: [
{
label: 'S',
},
{
label: 'M',
},
{
label: 'L',
},
],
},
],
} }
} }