Added basis setup and type generation for the products queries

This commit is contained in:
royderks 2021-04-16 16:28:52 +02:00 committed by Zaiste
parent ffe5a1c20e
commit b5e21a54b3
No known key found for this signature in database
GPG Key ID: 15DF7EBC7F2FFE35
25 changed files with 32173 additions and 14243 deletions

27
codegen.bigcommerce.json Normal file
View File

@ -0,0 +1,27 @@
{
"schema": {
"https://buybutton.store/graphql": {
"headers": {
"Authorization": "Bearer xzy"
}
}
},
"documents": [
{
"./framework/bigcommerce/api/**/*.ts": {
"noRequire": true
}
}
],
"generates": {
"./framework/bigcommerce/schema.d.ts": {
"plugins": ["typescript", "typescript-operations"]
},
"./framework/bigcommerce/schema.graphql": {
"plugins": ["schema-ast"]
}
},
"hooks": {
"afterAllFileWrite": ["prettier --write"]
}
}

View File

@ -1,23 +1,29 @@
{ {
"schema": { "schema": {
"https://buybutton.store/graphql": { "https://master.staging.saleor.cloud/graphql/": {}
"headers": {
"Authorization": "Bearer xzy"
}
}
}, },
"documents": [ "documents": [
{ {
"./framework/bigcommerce/api/**/*.ts": { "./framework/saleor/utils/queries/get-all-products-query.ts": {
"noRequire": true
}
},
{
"./framework/saleor/utils/queries/get-all-products-paths-query.ts": {
"noRequire": true
}
},
{
"./framework/saleor/utils/queries/get-products.ts": {
"noRequire": true "noRequire": true
} }
} }
], ],
"generates": { "generates": {
"./framework/bigcommerce/schema.d.ts": { "./framework/saleor/schema.d.ts": {
"plugins": ["typescript", "typescript-operations"] "plugins": ["typescript", "typescript-operations"]
}, },
"./framework/bigcommerce/schema.graphql": { "./framework/saleor/schema.graphql": {
"plugins": ["schema-ast"] "plugins": ["schema-ast"]
} }
}, },

View File

@ -1,5 +1,6 @@
{ {
"features": { "features": {
"wishlist": true "wishlist": false,
"customCheckout": false
} }
} }

View File

@ -23,7 +23,7 @@ const getAllPages = async (options?: {
config: SaleorConfig config: SaleorConfig
preview?: boolean preview?: boolean
}): Promise<ReturnType> => { }): Promise<ReturnType> => {
let { config, variables = { first: 250 } } = options ?? {} let { config, variables = { first: 100 } } = options ?? {}
config = getConfig(config) config = getConfig(config)
const { locale } = config const { locale } = config
const { data } = await config.fetch(getAllPagesQuery, { variables }) const { data } = await config.fetch(getAllPagesQuery, { variables })

View File

@ -7,17 +7,17 @@ const getAllCollections = async (options?: {
config: SaleorConfig config: SaleorConfig
preview?: boolean preview?: boolean
}) => { }) => {
let { config, variables = { first: 250 } } = options ?? {} let { config, variables = { first: 100 } } = options ?? {}
config = getConfig(config) config = getConfig(config)
const { data } = await config.fetch(getAllCollectionsQuery, { variables }) const { data } = await config.fetch(getAllCollectionsQuery, { variables })
const edges = data.collections?.edges ?? [] const edges = data.collections?.edges ?? []
const categories = edges.map( const categories = edges.map(
({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({ ({ node: { id: entityId, name, slug } }: CollectionEdge) => ({
entityId, entityId,
name, name,
path: `/${handle}`, path: `/${slug}`,
}) })
) )

View File

@ -21,7 +21,7 @@ const getAllProductPaths = async (options?: {
config?: SaleorConfig config?: SaleorConfig
preview?: boolean preview?: boolean
}): Promise<ReturnType> => { }): Promise<ReturnType> => {
let { config, variables = { first: 250 } } = options ?? {} let { config, variables = { first: 100 } } = options ?? {}
config = getConfig(config) config = getConfig(config)
const products = await fetchAllProducts({ const products = await fetchAllProducts({

View File

@ -1,6 +1,6 @@
import { GraphQLFetcherResult } from '@commerce/api' import { GraphQLFetcherResult } from '@commerce/api'
import { getConfig, SaleorConfig } from '../api' import { getConfig, SaleorConfig } from '../api'
import { ProductEdge } from '../schema' import { Product as SaleorProduct } from '../schema'
import { getAllProductsQuery } from '../utils/queries' import { getAllProductsQuery } from '../utils/queries'
import { normalizeProduct } from '../utils/normalize' import { normalizeProduct } from '../utils/normalize'
import { Product } from '@commerce/types' import { Product } from '@commerce/types'
@ -19,7 +19,7 @@ const getAllProducts = async (options: {
config?: SaleorConfig config?: SaleorConfig
preview?: boolean preview?: boolean
}): Promise<ReturnType> => { }): Promise<ReturnType> => {
let { config, variables = { first: 250 } } = options ?? {} let { config, variables = { first: 100 } } = options ?? {}
config = getConfig(config) config = getConfig(config)
const { data }: GraphQLFetcherResult = await config.fetch( const { data }: GraphQLFetcherResult = await config.fetch(
@ -28,7 +28,7 @@ const getAllProducts = async (options: {
) )
const products = const products =
data.products?.edges?.map(({ node: p }: ProductEdge) => data.products?.edges?.map(({ node: p }: SaleorProduct) =>
normalizeProduct(p) normalizeProduct(p)
) ?? [] ) ?? []

View File

@ -22,7 +22,7 @@ const getProduct = async (options: {
variables, variables,
}) })
const { productByHandle: product } = data const { product } = data
return { return {
product: product ? normalizeProduct(product) : null, product: product ? normalizeProduct(product) : null,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,16 +11,16 @@ export type Category = {
const getCategories = async (config: SaleorConfig): Promise<Category[]> => { const getCategories = async (config: SaleorConfig): Promise<Category[]> => {
const { data } = await config.fetch(getSiteCollectionsQuery, { const { data } = await config.fetch(getSiteCollectionsQuery, {
variables: { variables: {
first: 250, first: 100,
}, },
}) })
return ( return (
data.collections?.edges?.map( data.collections?.edges?.map(
({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({ ({ node: { id: entityId, name, slug } }: CollectionEdge) => ({
entityId, entityId,
name, name,
path: `/${handle}`, path: `/${slug}`,
}) })
) ?? [] ) ?? []
) )

View File

@ -14,27 +14,30 @@ export type BrandEdge = {
export type Brands = BrandEdge[] export type Brands = BrandEdge[]
// TODO: Find a way to get vendors from meta
const getVendors = async (config: SaleorConfig): Promise<BrandEdge[]> => { const getVendors = async (config: SaleorConfig): Promise<BrandEdge[]> => {
const vendors = await fetchAllProducts({ // const vendors = await fetchAllProducts({
config, // config,
query: getAllProductVendors, // query: getAllProductVendors,
variables: { // variables: {
first: 250, // first: 100,
}, // },
}) // })
let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor) // let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor)
return [...new Set(vendorsStrings)].map((v) => { // return [...new Set(vendorsStrings)].map((v) => {
const id = v.replace(/\s+/g, '-').toLowerCase() // const id = v.replace(/\s+/g, '-').toLowerCase()
return { // return {
node: { // node: {
entityId: id, // entityId: id,
name: v, // name: v,
path: `brands/${id}`, // path: `brands/${id}`,
}, // },
} // }
}) // })
return []
} }
export default getVendors export default getVendors

View File

@ -1,16 +1,17 @@
import { checkoutDetailsFragment } from '../queries/get-checkout-query' import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutCreateMutation = /* GraphQL */ ` const checkoutCreateMutation = /* GraphQL */ `
mutation { mutation createCheckout {
checkoutCreate(input: {}) { checkoutCreate(input: {}) {
checkoutUserErrors { checkoutUserErrors {
code code
field field
message message
} }
checkout { # Breaks GraphQL Codegen
${checkoutDetailsFragment} # checkout {
} # ${checkoutDetailsFragment}
# }
} }
} }
` `

View File

@ -1,16 +1,16 @@
import { checkoutDetailsFragment } from '../queries/get-checkout-query' import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutLineItemAddMutation = /* GraphQL */ ` const checkoutLineItemAddMutation = /* GraphQL */ `
mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemInput!]!) { mutation checkoutLineItemAdd($checkoutId: ID!, $lineItems: [CheckoutLineItemInput!]!) {
checkoutLineItemsAdd(checkoutId: $checkoutId, lineItems: $lineItems) { checkoutLineItemsAdd(checkoutId: $checkoutId, lineItems: $lineItems) {
checkoutUserErrors { checkoutUserErrors {
code code
field field
message message
} }
checkout { # checkout {
${checkoutDetailsFragment} # ${checkoutDetailsFragment}
} # }
} }
} }
` `

View File

@ -1,7 +1,7 @@
import { checkoutDetailsFragment } from '../queries/get-checkout-query' import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutLineItemRemoveMutation = /* GraphQL */ ` const checkoutLineItemRemoveMutation = /* GraphQL */ `
mutation($checkoutId: ID!, $lineItemIds: [ID!]!) { mutation checkoutLineItemRemove($checkoutId: ID!, $lineItemIds: [ID!]!) {
checkoutLineItemsRemove( checkoutLineItemsRemove(
checkoutId: $checkoutId checkoutId: $checkoutId
lineItemIds: $lineItemIds lineItemIds: $lineItemIds
@ -11,9 +11,9 @@ const checkoutLineItemRemoveMutation = /* GraphQL */ `
field field
message message
} }
checkout { # checkout {
${checkoutDetailsFragment} # ${checkoutDetailsFragment}
} # }
} }
} }
` `

View File

@ -1,16 +1,16 @@
import { checkoutDetailsFragment } from '../queries/get-checkout-query' import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutLineItemUpdateMutation = /* GraphQL */ ` const checkoutLineItemUpdateMutation = /* GraphQL */ `
mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemUpdateInput!]!) { mutation checkoutLineItemUpdate($checkoutId: ID!, $lineItems: [CheckoutLineItemUpdateInput!]!) {
checkoutLineItemsUpdate(checkoutId: $checkoutId, lineItems: $lineItems) { checkoutLineItemsUpdate(checkoutId: $checkoutId, lineItems: $lineItems) {
checkoutUserErrors { checkoutUserErrors {
code code
field field
message message
} }
checkout { # checkout {
${checkoutDetailsFragment} # ${checkoutDetailsFragment}
} # }
} }
} }
` `

View File

@ -9,67 +9,68 @@ import {
ProductVariantConnection, ProductVariantConnection,
MoneyV2, MoneyV2,
ProductOption, ProductOption,
Money,
} from '../schema' } from '../schema'
import type { Cart, LineItem } from '../types' import type { Cart, LineItem } from '../types'
const money = ({ amount, currencyCode }: MoneyV2) => { const money = ({ amount, currency }: Money) => {
return { return {
value: +amount, value: +amount,
currencyCode, currencyCode: currency || 'USD',
} }
} }
const normalizeProductOption = ({ const normalizeProductOptions = (options: ProductOption[]) => {
id, return options?.map(({ id, name: displayName, values }) => ({
name: displayName,
values,
}: ProductOption) => {
return {
__typename: 'MultipleChoiceOption', __typename: 'MultipleChoiceOption',
id, id,
displayName, displayName,
values: values.map((value) => { // values: values.map((value) => {
let output: any = { // let output: any = {
label: value, // label: value,
} // }
if (displayName.match(/colou?r/gi)) { // if (displayName.match(/colou?r/gi)) {
output = { // output = {
...output, // ...output,
hexColors: [value], // hexColors: [value],
} // }
} // }
return output // return output
}), // })
} values: [],
}))
} }
const normalizeProductImages = ({ edges }: ImageConnection) => const normalizeProductImages = (images: any) =>
edges?.map(({ node: { originalSrc: url, ...rest } }) => ({ images.map(({ node: { originalSrc: url, ...rest } }) => ({
url, url,
...rest, ...rest,
})) }))
const normalizeProductVariants = ({ edges }: ProductVariantConnection) => { const normalizeProductVariants = (variants: any) => {
return edges?.map( return variants?.map(
({ ({ id, selectedOptions, sku, name, priceV2, pricing }) => {
node: { id, selectedOptions, sku, title, priceV2, compareAtPriceV2 }, const price = money(pricing?.price?.net)?.value
}) => {
console.log({ price })
return { return {
id,
name: title,
sku: sku ?? id,
price: +priceV2.amount,
listPrice: +compareAtPriceV2?.amount,
requiresShipping: true,
options: selectedOptions.map(({ name, value }: SelectedOption) => {
const options = normalizeProductOption({
id, id,
name, name,
values: [value], sku: sku ?? id,
}) price,
return options listPrice: price,
}), requiresShipping: true,
// options: selectedOptions.map(({ name, value }: SelectedOption) => {
// const options = normalizeProductOption({
// id,
// name,
// values: [value],
// })
// return options
// }),
options: [],
} }
} }
) )
@ -78,28 +79,27 @@ const normalizeProductVariants = ({ edges }: ProductVariantConnection) => {
export function normalizeProduct(productNode: SaleorProduct): Product { export function normalizeProduct(productNode: SaleorProduct): Product {
const { const {
id, id,
title: name, name,
vendor, media,
images,
variants, variants,
description, description,
handle, slug,
priceRange, pricing,
options, // options,
...rest ...rest
} = productNode } = productNode
const product = { const product = {
id, id,
name, name,
vendor, vendor: '',
description, description,
path: `/${handle}`, path: `/${slug}`,
slug: handle?.replace(/^\/+|\/+$/g, ''), slug: slug?.replace(/^\/+|\/+$/g, ''),
price: money(priceRange?.minVariantPrice), price: money(pricing?.priceRange?.start?.net) || 0,
images: normalizeProductImages(images), images: media,
variants: variants ? normalizeProductVariants(variants) : [], variants: variants ? normalizeProductVariants(variants) : [],
options: options ? options.map((o) => normalizeProductOption(o)) : [], options: variants ? normalizeProductOptions(variants) : [],
...rest, ...rest,
} }

View File

@ -1,11 +1,11 @@
const getSiteCollectionsQuery = /* GraphQL */ ` const getSiteCollectionsQuery = /* GraphQL */ `
query getSiteCollections($first: Int!) { query getSiteCollections($first: Int!, $channel: String = "default-channel") {
collections(first: $first) { collections(first: $first, channel: $channel) {
edges { edges {
node { node {
id id
title name
handle slug
} }
} }
} }

View File

@ -1,11 +1,11 @@
export const getAllPagesQuery = /* GraphQL */ ` export const getAllPagesQuery = /* GraphQL */ `
query getAllPages($first: Int = 250) { query getAllPages($first: Int = 100) {
pages(first: $first) { pages(first: $first) {
edges { edges {
node { node {
id id
title title
handle slug
} }
} }
} }

View File

@ -1,13 +1,17 @@
const getAllProductsPathsQuery = /* GraphQL */ ` const getAllProductsPathsQuery = /* GraphQL */ `
query getAllProductPaths($first: Int = 250, $cursor: String) { query getAllProductPaths(
products(first: $first, after: $cursor) { $first: Int = 100
$cursor: String
$channel: String = "default-channel"
) {
products(first: $first, after: $cursor, channel: $channel) {
pageInfo { pageInfo {
hasNextPage hasNextPage
hasPreviousPage hasPreviousPage
} }
edges { edges {
node { node {
handle slug
} }
cursor cursor
} }

View File

@ -1,57 +1,42 @@
export const productConnection = ` export const productConnection = /* GraphQL */ `
pageInfo { fragment productConnnection on ProductCountableConnection {
hasNextPage
hasPreviousPage
}
edges {
node {
id
title
vendor
handle
description
priceRange {
minVariantPrice {
amount
currencyCode
}
}
images(first: 1) {
pageInfo { pageInfo {
hasNextPage hasNextPage
hasPreviousPage hasPreviousPage
} }
edges { edges {
node { node {
originalSrc id
altText name
width description
height slug
pricing {
priceRange {
start {
net {
amount
}
}
}
}
media {
url
alt
} }
} }
} }
} }
}`
export const productsFragment = `
products(
first: $first
sortKey: $sortKey
reverse: $reverse
query: $query
) {
${productConnection}
}
` `
const getAllProductsQuery = /* GraphQL */ ` const getAllProductsQuery = /* GraphQL */ `
query getAllProducts( query getAllProducts(
$first: Int = 250 $first: Int = 100
$query: String = "" $channel: String = "default-channel"
$sortKey: ProductSortKeys = RELEVANCE
$reverse: Boolean = false
) { ) {
${productsFragment} products(first: $first, channel: $channel) {
...productConnnection
} }
}
${productConnection}
` `
export default getAllProductsQuery export default getAllProductsQuery

View File

@ -1,66 +1,34 @@
const getProductQuery = /* GraphQL */ ` const getProductQuery = /* GraphQL */ `
query getProductBySlug($slug: String!) { query getProductBySlug($slug: String!, $channel: String = "default-channel") {
productByHandle(handle: $slug) { product(slug: $slug, channel: $channel) {
id id
handle slug
title name
productType
vendor
description description
descriptionHtml pricing {
options {
id
name
values
}
priceRange { priceRange {
maxVariantPrice { start {
net {
amount amount
currencyCode
}
minVariantPrice {
amount
currencyCode
} }
} }
variants(first: 250) {
pageInfo {
hasNextPage
hasPreviousPage
} }
edges { }
node { variants {
id id
title
sku
selectedOptions {
name name
value pricing {
} price {
priceV2 { net {
amount amount
currencyCode currency
}
compareAtPriceV2 {
amount
currencyCode
} }
} }
} }
} }
images(first: 250) { images {
pageInfo { url
hasNextPage alt
hasPreviousPage
}
edges {
node {
originalSrc
altText
width
height
}
}
} }
} }
} }

View File

@ -7,6 +7,7 @@ const {
const provider = commerce.provider || getProviderName() const provider = commerce.provider || getProviderName()
const isBC = provider === 'bigcommerce' const isBC = provider === 'bigcommerce'
const isShopify = provider === 'shopify' const isShopify = provider === 'shopify'
const isSaleor = provider === 'saleor'
const isSwell = provider === 'swell' const isSwell = provider === 'swell'
const isVendure = provider === 'vendure' const isVendure = provider === 'vendure'
@ -16,6 +17,9 @@ module.exports = withCommerceConfig({
locales: ['en-US', 'es'], locales: ['en-US', 'es'],
defaultLocale: 'en-US', defaultLocale: 'en-US',
}, },
images: {
domains: [process.env.COMMERCE_IMAGE_HOST],
},
rewrites() { rewrites() {
return [ return [
(isBC || isShopify || isSwell || isVendure) && { (isBC || isShopify || isSwell || isVendure) && {

View File

@ -22,8 +22,8 @@
"@components/*": ["components/*"], "@components/*": ["components/*"],
"@commerce": ["framework/commerce"], "@commerce": ["framework/commerce"],
"@commerce/*": ["framework/commerce/*"], "@commerce/*": ["framework/commerce/*"],
"@framework": ["framework/shopify"], "@framework": ["framework/saleor"],
"@framework/*": ["framework/shopify/*"] "@framework/*": ["framework/saleor/*"]
} }
}, },
"include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],

View File

@ -5517,11 +5517,6 @@ quick-lru@^4.0.1:
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==
quick-lru@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"