WIP POC in progress

This commit is contained in:
goncy 2021-08-09 18:11:00 -03:00
parent f765590484
commit 04f9799636
9 changed files with 143 additions and 67 deletions

View File

@ -1,4 +1,4 @@
# Available providers: bigcommerce, shopify, swell
# Available providers: bigcommerce, shopify, swell, local, saleor, vendure, ordercloud
COMMERCE_PROVIDER=
BIGCOMMERCE_STOREFRONT_API_URL=
@ -23,3 +23,6 @@ NEXT_PUBLIC_SALEOR_CHANNEL=
NEXT_PUBLIC_VENDURE_SHOP_API_URL=
NEXT_PUBLIC_VENDURE_LOCAL_URL=
NEXT_PUBLIC_ORDERCLOUD_CLIENT_ID=
NEXT_PUBLIC_ORDERCLOUD_CLIENT_SECRET=

View File

@ -20,7 +20,7 @@ export interface OrdercloudConfig extends Omit<CommerceAPIConfig, 'fetch'> {
}
const config: OrdercloudConfig = {
commerceUrl: 'https://sandboxapi.ordercloud.io/v1',
commerceUrl: 'https://sandboxapi.ordercloud.io',
apiToken: '',
cartCookie: '',
customerCookie: '',

View File

@ -1,12 +1,9 @@
import { Product } from '@commerce/types/product'
import { GetAllProductsOperation } from '@commerce/types/product'
import type { Product } from '@commerce/types/product'
import type { GetAllProductsOperation } from '@commerce/types/product'
import type { OperationContext } from '@commerce/api/operations'
import type { RawProduct } from '@framework/types/product'
import type { OrdercloudConfig, Provider } from '../index'
import {
PriceSchedule,
RawProduct,
RawProductWithPrice,
} from '@framework/types/product'
import { normalize as normalizeProduct } from '@framework/utils/product'
export default function getAllProductsOperation({
@ -26,19 +23,11 @@ export default function getAllProductsOperation({
'GET',
'/products'
).then((response) => response.Items)
const rawProductsWithPrice: RawProductWithPrice[] = await Promise.all(
rawProducts.map(async (product) => ({
...product,
priceSchedule: await fetch<PriceSchedule>(
'GET',
`/priceschedules/${product.ID}`
),
}))
)
return {
products: rawProductsWithPrice.map(normalizeProduct),
products: rawProducts.map(normalizeProduct),
}
}
return getAllProducts
}

View File

@ -1,24 +1,32 @@
import type { OrdercloudConfig } from '../index'
import { Product } from '@commerce/types/product'
import { GetProductOperation } from '@commerce/types/product'
import data from '../../data.json'
import type { OperationContext } from '@commerce/api/operations'
import type { RawProduct } from '@framework/types/product'
import type { Product } from '@commerce/types/product'
import type { GetProductOperation } from '@commerce/types/product'
import type { OrdercloudConfig, Provider } from '../index'
import { normalize as normalizeProduct } from '@framework/utils/product'
export default function getProductOperation({
commerce,
}: OperationContext<any>) {
}: OperationContext<Provider>) {
async function getProduct<T extends GetProductOperation>({
query = '',
variables,
config,
variables,
}: {
query?: string
variables?: T['variables']
config?: Partial<OrdercloudConfig>
preview?: boolean
} = {}): Promise<Product | {} | any> {
} = {}): Promise<{ product: Product }> {
const { fetch } = commerce.getConfig(config)
const rawProduct: RawProduct = await fetch<RawProduct>(
'GET',
`/products/${variables?.slug}`
)
return {
product: null // data.products.find(({ slug }) => slug === variables!.slug),
product: normalizeProduct(rawProduct),
}
}

View File

@ -18,26 +18,62 @@ const fetchRestApi: (
fetchOptions?: Record<string, any>
) => {
const { commerceUrl } = getConfig()
const res = await fetch(`${commerceUrl}${resource}`, {
// Check if we have a token stored
if (!global.token) {
// If not, get a new one and store it
const authResponse = await fetch(`${commerceUrl}/oauth/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
},
body: `client_id=${process.env.NEXT_PUBLIC_ORDERCLOUD_CLIENT_ID}&grant_type=client_credentials&client_secret=${process.env.NEXT_PUBLIC_ORDERCLOUD_CLIENT_SECRET}`,
})
// If something failed getting the auth response
if (!authResponse.ok) {
// Get the body of it
const error = await authResponse.json()
// And return an error
throw new FetcherError({
errors: [{ message: error.error_description.Code }],
status: error.error_description.HttpStatus,
})
}
// If everything is fine, store the access token in global.token
global.token = await authResponse
.json()
.then((response) => response.access_token)
}
// Do the request with the correct headers
const dataResponse = await fetch(`${commerceUrl}/v1${resource}`, {
...fetchOptions,
method,
headers: {
...fetchOptions?.headers,
accept: 'application/json, text/plain, */*',
'accept-language': 'es,en;q=0.9,es-ES;q=0.8,fr;q=0.7',
authorization: 'Bearer <your token>',
authorization: `Bearer ${global.token}`,
},
body: body ? JSON.stringify(body) : undefined,
})
if (!res.ok) {
// If something failed getting the data response
if (!dataResponse.ok) {
// Get the body of it
const error = await dataResponse.json()
// And return an error
throw new FetcherError({
errors: [{ message: res.statusText }],
status: res.status,
errors: [{ message: error.error_description.Code }],
status: error.error_description.HttpStatus,
})
}
return (await res.json()) as T
// Return the data and specify the expected type
return (await dataResponse.json()) as T
}
export default fetchRestApi

View File

@ -3,6 +3,15 @@ const commerce = require('./commerce.config.json')
module.exports = {
commerce,
images: {
domains: ['localhost'],
domains: [
'localhost',
// TODO: Remove this
'images.bloomingdalesassets.com',
'pbs.twimg.com',
'images.asos-media.com',
'di2ponv0v5otw.cloudfront.net',
'cdn.shopify.com',
'encrypted-tbn0.gstatic.com',
],
},
}

5
framework/ordercloud/types/node.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
declare module NodeJS {
interface Global {
token: string | null | undefined
}
}

View File

@ -17,28 +17,11 @@ export interface RawProduct {
Inventory: null
DefaultSupplierID: null
AllSuppliersCanSell: boolean
xp: null
}
export interface RawProductWithPrice extends RawProduct {
priceSchedule: PriceSchedule
}
export interface PriceSchedule {
OwnerID: string
ID: string
Name: string
ApplyTax: boolean
ApplyShipping: boolean
MinQuantity: number
MaxQuantity: number
UseCumulativeQuantity: boolean
RestrictedQuantity: boolean
PriceBreaks: [
{
Quantity: number
Price: number
}
]
xp: null
xp: {
Price: number
PriceCurrency: string
Images: {
url: string
}[]
}
}

View File

@ -1,17 +1,60 @@
import type { RawProductWithPrice } from '@framework/types/product'
import type { RawProduct } from '@framework/types/product'
import type { Product } from '@commerce/types/product'
export function normalize(product: RawProductWithPrice): Product {
export function normalize(product: RawProduct): Product {
return {
id: product.ID,
name: product.Name,
description: product.Description,
images: [],
variants: [],
slug: product.ID,
images: product.xp.Images,
price: {
value: product.priceSchedule.PriceBreaks[0].Price,
currencyCode: 'USD',
value: product.xp.Price,
currencyCode: product.xp.PriceCurrency,
},
options: [],
// TODO: Implement this
variants: [
{
id: 'unique',
options: [
{
id: 'unique',
displayName: 'Model',
values: [
{
label: 'Unique',
},
],
},
],
},
],
options: [
{
id: 'option-color',
displayName: 'Color',
values: [
{
label: 'color',
hexColors: ['#222'],
},
],
},
{
id: 'option-size',
displayName: 'Size',
values: [
{
label: 'S',
},
{
label: 'M',
},
{
label: 'L',
},
],
},
],
}
}