Update types (#831)

* Update product types

* Cart types progress, add zod & initial schema validator

* Update normalize.ts

* Update with-schema-parser.ts

* Updated types, schemas & providers

* Fix providers after schema parse errors

* Fix paths

* More provider fixes

* Fix kibocommerce & commercejs

* Add customer updated types & fixes

* Add checkout & customer types

* Import core types only from commerce

* Update tsconfig.json

* Convert hooks interfaces to types

* Requested changes

* Change to relative paths

* Move Zod dependency
This commit is contained in:
Catalin Pinte
2022-10-05 09:02:29 +03:00
committed by GitHub
parent 8398a96215
commit 6c2610584d
291 changed files with 1992 additions and 1808 deletions

View File

@@ -6,9 +6,12 @@ import * as Query from '../../utils/queries'
export type Page = any
export type GetAllPagesResult<T extends { pages: any[] } = { pages: Page[] }> = T
export type GetAllPagesResult<T extends { pages: any[] } = { pages: Page[] }> =
T
export default function getAllPagesOperation({ commerce }: OperationContext<Provider>) {
export default function getAllPagesOperation({
commerce,
}: OperationContext<Provider>) {
async function getAllPages({
query = Query.PageMany,
config,
@@ -34,11 +37,15 @@ export default function getAllPagesOperation({ commerce }: OperationContext<Prov
}
)
const pages = data.pages?.edges?.map(({ node: { title: name, slug, ...node } }: PageCountableEdge) => ({
...node,
url: `/${locale}/${slug}`,
name,
}))
const pages =
data?.pages?.edges?.map(
({ node: { title: name, slug, ...node } }: PageCountableEdge) => ({
id: node.id,
url: `/${locale}/${slug}`,
body: node.content || '',
name,
})
) ?? []
return { pages }
}

View File

@@ -3,15 +3,16 @@ import { ProductCountableEdge } from '../../../schema'
import type { Provider, SaleorConfig } from '..'
import { getAllProductsPathsQuery } from '../../utils/queries'
import fetchAllProducts from '../utils/fetch-all-products'
export type GetAllProductPathsResult = {
products: Array<{ path: string }>
}
export default function getAllProductPathsOperation({ commerce }: OperationContext<Provider>) {
export default function getAllProductPathsOperation({
commerce,
}: OperationContext<Provider>) {
async function getAllProductPaths({
query,
query = getAllProductsPathsQuery,
config,
variables,
}: {
@@ -21,16 +22,15 @@ export default function getAllProductPathsOperation({ commerce }: OperationConte
} = {}): Promise<GetAllProductPathsResult> {
config = commerce.getConfig(config)
const products = await fetchAllProducts({
config,
query: getAllProductsPathsQuery,
variables,
})
const { data }: any = await config.fetch(query, { variables })
return {
products: products?.map(({ node: { slug } }: ProductCountableEdge) => ({
path: `/${slug}`,
})),
products: data?.products?.edges?.map(
({ node: { slug } }: ProductCountableEdge) =>
({
path: `/${slug}`,
} ?? [])
),
}
}

View File

@@ -12,7 +12,9 @@ type ReturnType = {
products: Product[]
}
export default function getAllProductsOperation({ commerce }: OperationContext<Provider>) {
export default function getAllProductsOperation({
commerce,
}: OperationContext<Provider>) {
async function getAllProducts({
query = Query.ProductMany,
variables,
@@ -46,13 +48,18 @@ export default function getAllProductsOperation({ commerce }: OperationContext<P
if (featured) {
const products =
data.collection.products?.edges?.map(({ node: p }: ProductCountableEdge) => normalizeProduct(p)) ?? []
data?.collection.products?.edges?.map(
({ node: p }: ProductCountableEdge) => normalizeProduct(p)
) ?? []
return {
products,
}
} else {
const products = data.products?.edges?.map(({ node: p }: ProductCountableEdge) => normalizeProduct(p)) ?? []
const products =
data?.products?.edges?.map(({ node: p }: ProductCountableEdge) =>
normalizeProduct(p)
) ?? []
return {
products,

View File

@@ -8,7 +8,9 @@ export type Page = any
export type GetPageResult<T extends { page?: any } = { page?: Page }> = T
export default function getPageOperation({ commerce }: OperationContext<Provider>) {
export default function getPageOperation({
commerce,
}: OperationContext<Provider>) {
async function getPage({
query = Query.PageOne,
variables,
@@ -21,9 +23,7 @@ export default function getPageOperation({ commerce }: OperationContext<Provider
}): Promise<GetPageResult> {
const { fetch, locale = 'en-US' } = commerce.getConfig(config)
const {
data: { page },
} = await fetch(
const { data } = await fetch(
query,
{ variables },
{
@@ -36,13 +36,15 @@ export default function getPageOperation({ commerce }: OperationContext<Provider
)
return {
page: page
? {
...page,
name: page.title,
url: `/${locale}/${page.slug}`,
}
: null,
page:
data && data.page
? {
...data.page,
name: data.page.title,
body: data.page.content || '',
url: `/${locale}/${data.page.slug}`,
}
: null,
}
}

View File

@@ -12,7 +12,9 @@ type ReturnType = {
product: any
}
export default function getProductOperation({ commerce }: OperationContext<Provider>) {
export default function getProductOperation({
commerce,
}: OperationContext<Provider>) {
async function getProduct({
query = Query.ProductOneBySlug,
variables,
@@ -38,7 +40,7 @@ export default function getProductOperation({ commerce }: OperationContext<Provi
)
return {
product: data?.product ? normalizeProduct(data.product) : null,
product: data && data.product ? normalizeProduct(data.product) : null,
}
}

View File

@@ -1,41 +0,0 @@
import { ProductCountableEdge } from '../../../schema'
import { SaleorConfig } from '..'
const fetchAllProducts = async ({
config,
query,
variables,
acc = [],
cursor,
}: {
config: SaleorConfig
query: string
acc?: ProductCountableEdge[]
variables?: any
cursor?: string
}): Promise<ProductCountableEdge[]> => {
const { data } = await config.fetch(query, {
variables: { ...variables, cursor },
})
const edges: ProductCountableEdge[] = data.products?.edges ?? []
const hasNextPage = data.products?.pageInfo?.hasNextPage
acc = acc.concat(edges)
if (hasNextPage) {
const cursor = edges.pop()?.cursor
if (cursor) {
return fetchAllProducts({
config,
query,
variables,
acc,
cursor,
})
}
}
return acc
}
export default fetchAllProducts

View File

@@ -11,7 +11,7 @@ import useCart from './use-cart'
import * as mutation from '../utils/mutations'
import { getCheckoutId, checkoutToCart } from '../utils'
import { Mutation, MutationCheckoutLineDeleteArgs } from '../../schema'
import { LineItem, RemoveItemHook } from '../types/cart'
import type { LineItem, RemoveItemHook } from '@vercel/commerce/types/cart'
export default useRemoveItem as UseRemoveItem<typeof handler>

View File

@@ -1,19 +1,23 @@
import { useCallback } from 'react'
import debounce from 'lodash.debounce'
import type { HookFetcherContext, MutationHookContext } from '@vercel/commerce/utils/types'
import type {
HookFetcherContext,
MutationHookContext,
} from '@vercel/commerce/utils/types'
import { ValidationError } from '@vercel/commerce/utils/errors'
import useUpdateItem, { UseUpdateItem } from '@vercel/commerce/cart/use-update-item'
import useUpdateItem, {
UseUpdateItem,
} from '@vercel/commerce/cart/use-update-item'
import useCart from './use-cart'
import { handler as removeItemHandler } from './use-remove-item'
import type { LineItem } from '../types'
import { checkoutToCart } from '../utils'
import { getCheckoutId } from '../utils'
import { Mutation, MutationCheckoutLinesUpdateArgs } from '../../schema'
import * as mutation from '../utils/mutations'
import type { UpdateItemHook } from '../types/cart'
import type { UpdateItemHook, LineItem } from '@vercel/commerce/types/cart'
import type { Mutation, MutationCheckoutLinesUpdateArgs } from '../../schema'
export type UpdateItemActionInput<T = any> = T extends LineItem
? Partial<UpdateItemHook['actionInput']>
@@ -23,7 +27,11 @@ export default useUpdateItem as UseUpdateItem<typeof handler>
export const handler = {
fetchOptions: { query: mutation.CheckoutLineUpdate },
async fetcher({ input: { itemId, item }, options, fetch }: HookFetcherContext<UpdateItemHook>) {
async fetcher({
input: { itemId, item },
options,
fetch,
}: HookFetcherContext<UpdateItemHook>) {
if (Number.isInteger(item.quantity)) {
// Also allow the update hook to remove an item if the quantity is lower than 1
if (item.quantity! < 1) {
@@ -40,7 +48,10 @@ export const handler = {
}
const checkoutId = getCheckoutId().checkoutId
const { checkoutLinesUpdate } = await fetch<Mutation, MutationCheckoutLinesUpdateArgs>({
const { checkoutLinesUpdate } = await fetch<
Mutation,
MutationCheckoutLinesUpdateArgs
>({
...options,
variables: {
checkoutId,

View File

@@ -38,7 +38,7 @@ export const handler: SWRHook<SearchProductsHook> = {
let edges
if (categoryId) {
edges = data.collection?.products?.edges ?? []
edges = data?.collection?.products?.edges ?? []
// FIXME @zaiste, no `vendor` in Saleor
// if (brandId) {
// edges = edges.filter(
@@ -47,11 +47,13 @@ export const handler: SWRHook<SearchProductsHook> = {
// )
// }
} else {
edges = data.products?.edges ?? []
edges = data?.products?.edges ?? []
}
return {
products: edges.map(({ node }: ProductCountableEdge) => normalizeProduct(node)),
products: edges.map(({ node }: ProductCountableEdge) =>
normalizeProduct(node)
),
found: !!edges.length,
}
},

View File

@@ -1,32 +0,0 @@
import * as Core from '@vercel/commerce/types/cart'
export * from '@vercel/commerce/types/cart'
export type SaleorCart = {}
/**
* Extend core cart types
*/
export type Cart = Core.Cart & {
lineItems: Core.LineItem[]
url?: string
}
export type CartTypes = Core.CartTypes
export type CartHooks = Core.CartHooks<CartTypes>
export type GetCartHook = CartHooks['getCart']
export type AddItemHook = CartHooks['addItem']
export type UpdateItemHook = CartHooks['updateItem']
export type RemoveItemHook = CartHooks['removeItem']
export type CartSchema = Core.CartSchema<CartTypes>
export type CartHandlers = Core.CartHandlers<CartTypes>
export type GetCartHandler = CartHandlers['getCart']
export type AddItemHandler = CartHandlers['addItem']
export type UpdateItemHandler = CartHandlers['updateItem']
export type RemoveItemHandler = CartHandlers['removeItem']

View File

@@ -6,17 +6,19 @@ import * as query from './queries'
const getCategories = async (config: SaleorConfig): Promise<Category[]> => {
const { data } = await config.fetch(query.CollectionMany, {
variables: {
first: 100,
first: 50,
},
})
return (
data.collections?.edges?.map(({ node: { id, name, slug } }: CollectionCountableEdge) => ({
id,
name,
slug,
path: `/${slug}`,
})) ?? []
data?.collections?.edges?.map(
({ node: { id, name, slug } }: CollectionCountableEdge) => ({
id,
name,
slug,
path: `/${slug}`,
})
) ?? []
)
}

View File

@@ -30,7 +30,7 @@ const getVendors = async (config: SaleorConfig): Promise<BrandEdge[]> => {
// node: {
// entityId: id,
// name: v,
// path: `brands/${id}`,
// path: `/${id}`,
// },
// }
// })

View File

@@ -1,6 +1,12 @@
import { Product } from '@vercel/commerce/types/product'
import { Product as SaleorProduct, Checkout, CheckoutLine, Money, ProductVariant } from '../../schema'
import {
Product as SaleorProduct,
Checkout,
CheckoutLine,
Money,
ProductVariant,
} from '../../schema'
import type { Cart, LineItem } from '../types'
@@ -19,11 +25,14 @@ const normalizeProductOptions = (options: ProductVariant[]) => {
?.map((option) => option?.attributes)
.flat(1)
.reduce<any>((acc, x) => {
if (acc.find(({ displayName }: any) => displayName === x.attribute.name)) {
if (
acc.find(({ displayName }: any) => displayName === x.attribute.name)
) {
return acc.map((opt: any) => {
return opt.displayName === x.attribute.name
? {
...opt,
id: x.attribute.id,
values: [
...opt.values,
...x.values.map((value: any) => ({
@@ -37,6 +46,7 @@ const normalizeProductOptions = (options: ProductVariant[]) => {
return acc.concat({
__typename: 'MultipleChoiceOption',
id: x.attribute.id,
displayName: x.attribute.name,
variant: 'size',
values: x.values.map((value: any) => ({
@@ -54,7 +64,7 @@ const normalizeProductVariants = (variants: ProductVariant[]) => {
return {
id,
name,
sku: sku ?? id,
...(!!sku && { sku }),
price,
listPrice: price,
requiresShipping: true,
@@ -64,23 +74,41 @@ const normalizeProductVariants = (variants: ProductVariant[]) => {
}
export function normalizeProduct(productNode: SaleorProduct): Product {
const { id, name, media = [], variants, description, slug, pricing, ...rest } = productNode
const {
id,
name,
media = [],
variants,
description,
slug,
pricing,
...rest
} = productNode
const product = {
id,
name,
vendor: '',
description: description ? JSON.parse(description)?.blocks[0]?.data.text : '',
description: description
? JSON.parse(description)?.blocks[0]?.data.text
: '',
path: `/${slug}`,
slug: slug?.replace(/^\/+|\/+$/g, ''),
price: (pricing?.priceRange?.start?.net && money(pricing.priceRange.start.net)) || {
price: (pricing?.priceRange?.start?.net &&
money(pricing.priceRange.start.net)) || {
value: 0,
currencyCode: 'USD',
},
// TODO: Check nextjs-commerce bug if no images are added for a product
images: media?.length ? media : [{ url: placeholderImg }],
variants: variants && variants.length > 0 ? normalizeProductVariants(variants as ProductVariant[]) : [],
options: variants && variants.length > 0 ? normalizeProductOptions(variants as ProductVariant[]) : [],
variants:
variants && variants.length > 0
? normalizeProductVariants(variants as ProductVariant[])
: [],
options:
variants && variants.length > 0
? normalizeProductOptions(variants as ProductVariant[])
: [],
...rest,
}
@@ -89,7 +117,8 @@ export function normalizeProduct(productNode: SaleorProduct): Product {
export function normalizeCart(checkout: Checkout): Cart {
const lines = checkout.lines as CheckoutLine[]
const lineItems: LineItem[] = lines.length > 0 ? lines?.map<LineItem>(normalizeLineItem) : []
const lineItems: LineItem[] =
lines.length > 0 ? lines?.map<LineItem>(normalizeLineItem) : []
return {
id: checkout.id,
@@ -117,7 +146,7 @@ function normalizeLineItem({ id, variant, quantity }: CheckoutLine): LineItem {
quantity,
variant: {
id: String(variant?.id),
sku: variant?.sku ?? '',
...(variant?.sku && { sku: variant.sku }),
name: variant?.name!,
image: {
url: variant?.media![0] ? variant?.media![0].url : placeholderImg,

View File

@@ -1,7 +1,11 @@
import * as fragment from '../fragments'
export const CollectionOne = /* GraphQL */ `
query getProductsFromCollection($categoryId: ID!, $first: Int = 100, $channel: String = "default-channel") {
query getProductsFromCollection(
$categoryId: ID!
$first: Int = 50
$channel: String = "default-channel"
) {
collection(id: $categoryId, channel: $channel) {
id
products(first: $first) {

View File

@@ -1,5 +1,5 @@
export const getAllProductVendors = /* GraphQL */ `
query getAllProductVendors($first: Int = 250, $cursor: String) {
query getAllProductVendors($first: Int = 50, $cursor: String) {
products(first: $first, after: $cursor) {
pageInfo {
hasNextPage

View File

@@ -1,5 +1,9 @@
export const getAllProductsPathsQuery = /* GraphQL */ `
query getAllProductPaths($first: Int = 100, $cursor: String, $channel: String = "default-channel") {
query getAllProductPaths(
$first: Int = 50
$cursor: String
$channel: String = "default-channel"
) {
products(first: $first, after: $cursor, channel: $channel) {
pageInfo {
hasNextPage

View File

@@ -1,5 +1,5 @@
export const PageMany = /* GraphQL */ `
query PageMany($first: Int = 100) {
query PageMany($first: Int = 50) {
pages(first: $first) {
edges {
node {

View File

@@ -2,12 +2,17 @@ import * as fragment from '../fragments'
export const ProductMany = /* GraphQL */ `
query ProductMany(
$first: Int = 100
$first: Int = 50
$filter: ProductFilterInput
$sortBy: ProductOrder
$channel: String = "default-channel"
) {
products(first: $first, channel: $channel, filter: $filter, sortBy: $sortBy) {
products(
first: $first
channel: $channel
filter: $filter
sortBy: $sortBy
) {
...ProductConnection
}
}

View File

@@ -19,6 +19,7 @@ export const ProductOneBySlug = /* GraphQL */ `
name
attributes {
attribute {
id
name
}
values {