Moved product, all products (paths)

This commit is contained in:
cond0r 2021-05-25 10:12:12 +03:00
parent 15d6ff6709
commit c2ff5b3616
18 changed files with 257 additions and 266 deletions

View File

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

View File

@ -8,10 +8,7 @@ import { API_URL, API_TOKEN, SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '../const'
import fetchGraphqlApi from './utils/fetch-graphql-api' import fetchGraphqlApi from './utils/fetch-graphql-api'
import login from './operations/login' import * as operations from './operations'
import getAllPages from './operations/get-all-pages'
import getPage from './operations/get-page'
import getSiteInfo from './operations/get-site-info'
if (!API_URL) { if (!API_URL) {
throw new Error( throw new Error(
@ -24,46 +21,22 @@ if (!API_TOKEN) {
`The environment variable NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store` `The environment variable NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is missing and it's required to access your store`
) )
} }
export interface ShopifyConfig extends CommerceAPIConfig { export interface ShopifyConfig extends CommerceAPIConfig {}
applyLocale?: boolean
}
export class Config {
private config: ShopifyConfig
constructor(config: ShopifyConfig) {
this.config = config
}
getConfig(userConfig: Partial<ShopifyConfig> = {}) {
return Object.entries(userConfig).reduce<ShopifyConfig>(
(cfg, [key, value]) => Object.assign(cfg, { [key]: value }),
{ ...this.config }
)
}
setConfig(newConfig: Partial<ShopifyConfig>) {
Object.assign(this.config, newConfig)
}
}
const ONE_DAY = 60 * 60 * 24 const ONE_DAY = 60 * 60 * 24
const configJson: ShopifyConfig = { const config: ShopifyConfig = {
commerceUrl: API_URL, commerceUrl: API_URL,
apiToken: API_TOKEN, apiToken: API_TOKEN,
customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE, customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE,
cartCookie: process.env.SHOPIFY_CART_COOKIE ?? 'shopify_checkoutId', cartCookie: process.env.SHOPIFY_CART_COOKIE ?? 'shopify_checkoutId',
cartCookieMaxAge: ONE_DAY * 30, cartCookieMaxAge: ONE_DAY * 30,
fetch: fetchGraphqlApi, fetch: fetchGraphqlApi,
applyLocale: true,
} }
const config = new Config(configJson)
export const provider = { export const provider = {
config: configJson, config,
operations: { getSiteInfo, getPage, getAllPages, login }, operations,
} }
export type Provider = typeof provider export type Provider = typeof provider
@ -73,13 +46,6 @@ export type ShopifyAPI<P extends Provider = Provider> = CommerceAPI<P>
export function getCommerceApi<P extends Provider>( export function getCommerceApi<P extends Provider>(
customProvider: P = provider as any customProvider: P = provider as any
): ShopifyAPI<P> { ): ShopifyAPI<P> {
console.log(customProvider)
return commerceApi(customProvider) return commerceApi(customProvider)
} }
export function getConfig(userConfig?: Partial<ShopifyConfig>) {
return config.getConfig(userConfig)
}
export function setConfig(newConfig: Partial<ShopifyConfig>) {
return config.setConfig(newConfig)
}

View File

@ -1,23 +1,39 @@
import type { OperationContext } from '@commerce/api/operations' import type {
OperationContext,
OperationOptions,
} from '@commerce/api/operations'
import { GetAllPagesQuery, GetAllPagesQueryVariables } from '@framework/schema' import { GetAllPagesQuery, GetAllPagesQueryVariables } from '@framework/schema'
import type { ShopifyConfig, Provider } from '..' import type { ShopifyConfig, Provider } from '..'
import { GetAllPagesOperation } from '../../types/page' import { GetAllPagesOperation } from '../../types/page'
import getAllPagesQuery from '../../utils/queries/get-all-pages-query' import getAllPagesQuery from '../../utils/queries/get-all-pages-query'
export default function getSiteInfoOperation({ export default function getAllPagesOperation({
commerce, commerce,
}: OperationContext<Provider>) { }: OperationContext<Provider>) {
async function getPage<T extends GetAllPagesOperation>({ async function getAllPages<T extends GetAllPagesOperation>(opts?: {
config?: Partial<ShopifyConfig>
preview?: boolean
}): Promise<T['data']>
async function getAllPages<T extends GetAllPagesOperation>(
opts: {
config?: Partial<ShopifyConfig>
preview?: boolean
} & OperationOptions
): Promise<T['data']>
async function getAllPages<T extends GetAllPagesOperation>({
query = getAllPagesQuery, query = getAllPagesQuery,
config, config,
}: { }: {
query?: string url?: string
config?: ShopifyConfig config?: Partial<ShopifyConfig>
preview?: boolean preview?: boolean
query?: string
} = {}): Promise<T['data']> { } = {}): Promise<T['data']> {
config = commerce.getConfig(config) const cfg = commerce.getConfig(config)
const { data } = await config.fetch< const { data } = await cfg.fetch<
GetAllPagesQuery, GetAllPagesQuery,
GetAllPagesQueryVariables GetAllPagesQueryVariables
>(query) >(query)
@ -27,5 +43,5 @@ export default function getSiteInfoOperation({
} }
} }
return getPage return getAllPages
} }

View File

@ -0,0 +1,55 @@
import type {
OperationContext,
OperationOptions,
} from '@commerce/api/operations'
import { GetAllProductPathsOperation } from '../../types/product'
import {
GetAllProductPathsQuery,
GetAllProductPathsQueryVariables,
ProductEdge,
} from '../../schema'
import type { ShopifyConfig, Provider } from '..'
import { getAllProductsQuery } from '../../utils'
export default function getAllProductPathsOperation({
commerce,
}: OperationContext<Provider>) {
async function getAllProductPaths<
T extends GetAllProductPathsOperation
>(opts?: {
variables?: T['variables']
config?: ShopifyConfig
}): Promise<T['data']>
async function getAllProductPaths<T extends GetAllProductPathsOperation>(
opts: {
variables?: T['variables']
config?: ShopifyConfig
} & OperationOptions
): Promise<T['data']>
async function getAllProductPaths<T extends GetAllProductPathsOperation>({
query = getAllProductsQuery,
config,
variables,
}: {
query?: string
config?: ShopifyConfig
variables?: T['variables']
} = {}): Promise<T['data']> {
config = commerce.getConfig(config)
const { data } = await config.fetch<
GetAllProductPathsQuery,
GetAllProductPathsQueryVariables
>(query, { variables })
return {
products: data.products.edges.map(({ node: { handle } }) => ({
path: `/${handle}`,
})),
}
}
return getAllProductPaths
}

View File

@ -0,0 +1,57 @@
import type {
OperationContext,
OperationOptions,
} from '@commerce/api/operations'
import { GetAllProductsOperation } from '../../types/product'
import {
GetAllProductsQuery,
GetAllProductsQueryVariables,
Product as ShopifyProduct,
} from '../../schema'
import type { ShopifyConfig, Provider } from '..'
import getAllProductsQuery from '../../utils/queries/get-all-products-query'
import { normalizeProduct } from '@framework/utils'
export default function getAllProductsOperation({
commerce,
}: OperationContext<Provider>) {
async function getAllProducts<T extends GetAllProductsOperation>(opts?: {
variables?: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean
}): Promise<T['data']>
async function getAllProducts<T extends GetAllProductsOperation>(
opts: {
variables?: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean
} & OperationOptions
): Promise<T['data']>
async function getAllProducts<T extends GetAllProductsOperation>({
query = getAllProductsQuery,
variables,
config,
}: {
query?: string
variables?: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean
} = {}): Promise<T['data']> {
const cfg = commerce.getConfig(config)
const { data } = await cfg.fetch<
GetAllProductsQuery,
GetAllProductsQueryVariables
>(query, { variables })
return {
products: data.products.edges.map(({ node }) =>
normalizeProduct(node as ShopifyProduct)
),
}
}
return getAllProducts
}

View File

@ -1,32 +1,47 @@
import type { OperationContext } from '@commerce/api/operations' import type {
OperationContext,
OperationOptions,
} from '@commerce/api/operations'
import type { ShopifyConfig, Provider } from '..' import type { ShopifyConfig, Provider } from '..'
import { GetPageQuery, GetPageQueryVariables, Page } from '../../schema' import { GetPageQuery, GetPageQueryVariables, Page } from '../../schema'
import { GetPageOperation } from '../../types/page' import { GetPageOperation } from '../../types/page'
import getPageQuery from '../../utils/queries/get-page-query' import getPageQuery from '../../utils/queries/get-page-query'
export default function getSiteInfoOperation({ export default function getPageOperation({
commerce, commerce,
}: OperationContext<Provider>) { }: OperationContext<Provider>) {
async function getPage<T extends GetPageOperation>(opts: {
variables: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean
}): Promise<T['data']>
async function getPage<T extends GetPageOperation>(
opts: {
variables: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean
} & OperationOptions
): Promise<T['data']>
async function getPage<T extends GetPageOperation>({ async function getPage<T extends GetPageOperation>({
query = getPageQuery, query = getPageQuery,
variables,
config, config,
preview,
}: { }: {
query?: string query?: string
config?: ShopifyConfig variables: T['variables']
config?: Partial<ShopifyConfig>
preview?: boolean preview?: boolean
} = {}): Promise<T['data']> { }): Promise<T['data']> {
config = commerce.getConfig(config) const cfg = commerce.getConfig(config)
const { data } = await config.fetch<GetPageQuery, GetPageQueryVariables>( const {
query data: { node: page },
) } = await cfg.fetch<GetPageQuery, GetPageQueryVariables>(query)
if (data.node) { return page ? { page } : {}
return {}
}
return {}
} }
return getPage return getPage

View File

@ -0,0 +1,39 @@
import type { OperationContext } from '@commerce/api/operations'
import { GetProductOperation } from '../../types/product'
import { normalizeProduct, getProductQuery } from '../../utils'
import type { ShopifyConfig, Provider } from '..'
import {
GetProductBySlugQuery,
GetProductBySlugQueryVariables,
Product as ShopifyProduct,
} from '../../schema'
export default function getProductOperation({
commerce,
}: OperationContext<Provider>) {
async function getProduct<T extends GetProductOperation>({
query = getProductQuery,
config,
variables,
}: {
query?: string
config?: ShopifyConfig
variables?: GetProductBySlugQueryVariables
} = {}): Promise<T['data']> {
config = commerce.getConfig(config)
const {
data: { productByHandle },
} = await config.fetch<GetProductBySlugQuery>(query, {
variables,
})
return {
...(productByHandle && {
product: normalizeProduct(productByHandle as ShopifyProduct),
}),
}
}
return getProduct
}

View File

@ -1,28 +1,45 @@
import type { OperationContext } from '@commerce/api/operations' import type {
import type { GetSiteInfoQuery } from '../../schema' OperationContext,
OperationOptions,
} from '@commerce/api/operations'
import { GetSiteInfoQuery, GetSiteInfoQueryVariables } from '@framework/schema'
import type { ShopifyConfig, Provider } from '..' import type { ShopifyConfig, Provider } from '..'
import { GetSiteInfoOperation } from '../../types/site' import { GetSiteInfoOperation } from '../../types/site'
import getSiteInfoQuery from '../../utils/queries/get-site-info-query' import { getCategories, getBrands, getSiteInfoQuery } from '../../utils'
import { getCategories, getVendors } from '@framework/utils'
export default function getSiteInfoOperation({ export default function getSiteInfoOperation({
commerce, commerce,
}: OperationContext<Provider>) { }: OperationContext<Provider>) {
async function getSiteInfo<T extends GetSiteInfoOperation>(opts?: {
config?: Partial<ShopifyConfig>
preview?: boolean
}): Promise<T['data']>
async function getSiteInfo<T extends GetSiteInfoOperation>(
opts: {
config?: Partial<ShopifyConfig>
preview?: boolean
} & OperationOptions
): Promise<T['data']>
async function getSiteInfo<T extends GetSiteInfoOperation>({ async function getSiteInfo<T extends GetSiteInfoOperation>({
query = getSiteInfoQuery, query = getSiteInfoQuery,
config, config,
}: { }: {
query?: string query?: string
config?: ShopifyConfig config?: Partial<ShopifyConfig>
preview?: boolean preview?: boolean
} = {}): Promise<T['data']> { } = {}): Promise<T['data']> {
config = commerce.getConfig(config) const cfg = commerce.getConfig(config)
const categories = await getCategories(config) const categories = await getCategories(cfg)
const brands = await getVendors(config) const brands = await getBrands(cfg)
// const { data } = await config.fetch<GetSiteInfoQuery>(query) const { data } = await cfg.fetch<
GetSiteInfoQuery,
GetSiteInfoQueryVariables
>(query)
return { return {
categories, categories,

View File

@ -0,0 +1,7 @@
export { default as getAllPages } from './get-all-pages'
export { default as getPage } from './get-page'
export { default as getAllProducts } from './get-all-products'
export { default as getAllProductPaths } from './get-all-product-paths'
export { default as getProduct } from './get-product'
export { default as getSiteInfo } from './get-site-info'
export { default as login } from './login'

View File

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

View File

@ -1,31 +0,0 @@
import { GetSiteCollectionsQuery } from '../schema'
import { getConfig, ShopifyConfig } from '../api'
import getAllCollectionsQuery from '../utils/queries/get-all-collections-query'
const getAllCollections = async (options?: {
variables?: any
config: ShopifyConfig
preview?: boolean
}) => {
let { config, variables = { first: 250 } } = options ?? {}
config = getConfig(config)
const { data } = await config.fetch<GetSiteCollectionsQuery>(
getAllCollectionsQuery,
{ variables }
)
const categories = data.collections.edges.map(
({ node: { id: entityId, title: name, handle } }) => ({
entityId,
name,
path: `/${handle}`,
})
)
return {
categories,
}
}
export default getAllCollections

View File

@ -1,41 +0,0 @@
import { getConfig, ShopifyConfig } from '../api'
import { ProductEdge } from '../schema'
import getAllProductsPathsQuery from '../utils/queries/get-all-products-paths-query'
type ProductPath = {
path: string
}
export type ProductPathNode = {
node: ProductPath
}
type ReturnType = {
products: ProductPathNode[]
}
const getAllProductPaths = async (options?: {
variables?: any
config?: ShopifyConfig
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables = { first: 100, sortKey: 'BEST_SELLING' } } =
options ?? {}
config = getConfig(config)
const { data } = await config.fetch(getAllProductsPathsQuery, {
variables,
})
return {
products: data.products?.edges?.map(
({ node: { handle } }: ProductEdge) => ({
node: {
path: `/${handle}`,
},
})
),
}
}
export default getAllProductPaths

View File

@ -1,34 +0,0 @@
import { getConfig, ShopifyConfig } from '../api'
import { GetAllProductsQuery, Product as ShopifyProduct } from '../schema'
import { getAllProductsQuery } from '../utils/queries'
import { normalizeProduct } from '../utils/normalize'
import { Product } from '../types/product'
type Variables = {
first?: number
field?: string
}
const getAllProducts = async (options: {
variables?: Variables
config?: ShopifyConfig
preview?: boolean
}): Promise<{
products: Product[]
}> => {
let { config, variables = { first: 250 } } = options ?? {}
config = getConfig(config)
const { data } = await config.fetch<GetAllProductsQuery>(
getAllProductsQuery,
{ variables }
)
return {
products: data.products.edges.map(({ node }) =>
normalizeProduct(node as ShopifyProduct)
),
}
}
export default getAllProducts

View File

@ -1,33 +0,0 @@
import { GetProductBySlugQuery, Product as ShopifyProduct } from '../schema'
import { getConfig, ShopifyConfig } from '../api'
import { normalizeProduct, getProductQuery } from '../utils'
import { Product } from '@commerce/types'
type Variables = {
slug: string
}
const getProduct = async (options: {
variables: Variables
config: ShopifyConfig
preview?: boolean
}): Promise<{
product?: Product
}> => {
let { config, variables } = options ?? {}
config = getConfig(config)
const {
data: { productByHandle },
} = await config.fetch<GetProductBySlugQuery>(getProductQuery, {
variables,
})
return {
...(productByHandle && {
product: normalizeProduct(productByHandle as ShopifyProduct),
}),
}
}
export default getProduct

View File

@ -1,5 +1,8 @@
import {
GetAllProductVendorsQuery,
GetAllProductVendorsQueryVariables,
} from '@framework/schema'
import { ShopifyConfig } from '../api' import { ShopifyConfig } from '../api'
import fetchAllProducts from '../api/utils/fetch-all-products'
import getAllProductVendors from './queries/get-all-product-vendors-query' import getAllProductVendors from './queries/get-all-product-vendors-query'
export type Brand = { export type Brand = {
@ -14,16 +17,17 @@ export type BrandEdge = {
export type Brands = BrandEdge[] export type Brands = BrandEdge[]
const getVendors = async (config: ShopifyConfig): Promise<BrandEdge[]> => { const getBrands = async (config: ShopifyConfig): Promise<BrandEdge[]> => {
const vendors = await fetchAllProducts({ const { data } = await config.fetch<
config, GetAllProductVendorsQuery,
query: getAllProductVendors, GetAllProductVendorsQueryVariables
>(getAllProductVendors, {
variables: { variables: {
first: 250, first: 250,
}, },
}) })
let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor) let vendorsStrings = data.products.edges.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()
@ -37,4 +41,4 @@ const getVendors = async (config: ShopifyConfig): Promise<BrandEdge[]> => {
}) })
} }
export default getVendors export default getBrands

View File

@ -1,7 +1,7 @@
export { default as handleFetchResponse } from './handle-fetch-response' export { default as handleFetchResponse } from './handle-fetch-response'
export { default as getSearchVariables } from './get-search-variables' export { default as getSearchVariables } from './get-search-variables'
export { default as getSortVariables } from './get-sort-variables' export { default as getSortVariables } from './get-sort-variables'
export { default as getVendors } from './get-vendors' export { default as getBrands } from './get-brands'
export { default as getCategories } from './get-categories' export { default as getCategories } from './get-categories'
export { default as getCheckoutId } from './get-checkout-id' export { default as getCheckoutId } from './get-checkout-id'
export { default as checkoutCreate } from './checkout-create' export { default as checkoutCreate } from './checkout-create'

View File

@ -8,3 +8,4 @@ export { default as getCheckoutQuery } from './get-checkout-query'
export { default as getAllPagesQuery } from './get-all-pages-query' export { default as getAllPagesQuery } from './get-all-pages-query'
export { default as getPageQuery } from './get-page-query' export { default as getPageQuery } from './get-page-query'
export { default as getCustomerQuery } from './get-customer-query' export { default as getCustomerQuery } from './get-customer-query'
export { default as getSiteInfoQuery } from './get-site-info-query'

View File

@ -15,7 +15,6 @@ export async function getStaticProps({
config, config,
preview, preview,
}) })
console.log(commerce)
const { categories, brands } = await commerce.getSiteInfo({ config, preview }) const { categories, brands } = await commerce.getSiteInfo({ config, preview })
const { pages } = await commerce.getAllPages({ config, preview }) const { pages } = await commerce.getAllPages({ config, preview })