Skip to content

Commit

Permalink
Merge pull request #2379 from graphcommerce-org/fix/various
Browse files Browse the repository at this point in the history
Fix/various
  • Loading branch information
paales authored Oct 7, 2024
2 parents 51bf515 + ce30678 commit 477c3d0
Show file tree
Hide file tree
Showing 17 changed files with 140 additions and 70 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilly-nails-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphcommerce/magento-customer': patch
---

In some cases the xMagentoCacheId wasn't defined in the returned query, make sure the application doesn't crash
5 changes: 5 additions & 0 deletions .changeset/grumpy-spies-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphcommerce/next-config': patch
---

All automatically generated interceptor files are now read-only in vscode to prevent accedental changes.
5 changes: 5 additions & 0 deletions .changeset/new-squids-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphcommerce/magento-recently-viewed-products': patch
---

Solved issue where Recently Viewed Products would execute a query even if there were no products to display.
5 changes: 5 additions & 0 deletions .changeset/thin-teachers-try.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphcommerce/graphql': patch
---

When a useInContextQuery is called, only execute when there is no InContextMaskContext defined above
5 changes: 5 additions & 0 deletions .changeset/tidy-timers-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphcommerce/algolia-recommend': patch
---

Automatically fall back to existing upsells/related products if they are defined and Algolia returns an error
5 changes: 5 additions & 0 deletions .changeset/young-sloths-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphcommerce/magento-product': patch
---

Render multiple items in the RowSpecs table as a list
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
"yarn.lock": true,
"**/*.tsbuildinfo": true
},
"files.readonlyInclude": {
"**/*.interceptor.tsx": true,
"**/*.interceptor.ts": true
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
Expand Down
4 changes: 4 additions & 0 deletions examples/magento-graphcms/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,9 @@
".yarn": true,
"yarn.lock": true
},
"files.readonlyInclude": {
"**/*.interceptor.tsx": true,
"**/*.interceptor.ts": true
},
"typescript.enablePromptUseWorkspaceTsdk": true
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export function algoliaHitToMagentoProduct(
// options_container: null,
// price_tiers: [],
// product_links: [],
// related_products: getRecommendations(),
// related_products: null,
// short_description: null,
// small_image: null,
// special_price: null,
Expand Down
98 changes: 56 additions & 42 deletions packages/algolia-recommend/mesh/getRecommendations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,25 @@ import type {
AlgoliarecommendationsHit,
} from '@graphcommerce/graphql-mesh'
import { nonNullable } from '@graphcommerce/next-ui'
import type { GraphQLResolveInfo } from 'graphql'
import type { GraphQLError, GraphQLResolveInfo } from 'graphql'
import type { Simplify } from 'type-fest'

function isGraphQLError(err: unknown): err is GraphQLError {
return !!(err as GraphQLError)?.message
}

const inputToModel = {
Trending_items_Input: 'trending_items' as const,
Trending_facet_values_Input: 'trending_facets' as const,
Frequently_bought_together_Input: 'bought_together' as const,
Looking_similar_Input: 'looking_similar' as const,
Related_products_Input: 'related_products' as const,
}

function isAlgoliaResponse<T extends object>(root: T): root is T & { uid: string } {
return 'uid' in root
}
function argsFromKeysInput(keys, args, context) {
const body = keys
.map(
(key) =>
({
[key.keyInput]: {
model: inputToModel[key.keyInput as string],
indexName: getIndexName(context),

...args,
objectID: key.objectId,
},
}) as unknown as AlgoliarecommendationsRequest_Input,
)
.filter(nonNullable)

const returnObject = { input: { requests: body } }

return returnObject
}
export async function getRecommendations<
K extends keyof AlgoliarecommendationsRequest_Input,
Input extends AlgoliarecommendationsRequest_Input[K],
Expand All @@ -53,29 +39,57 @@ export async function getRecommendations<
if (!isAlgoliaResponse(root)) {
return []
}
return (
(await context.algoliaRecommend.Query.algolia_getRecommendations({
key: { keyInput, objectId: atob(root.uid) },
argsFromKeys: (keys) => argsFromKeysInput(keys, args, context),
valuesFromResults: (res, keys) =>
keys
.map((_key, index) => res?.results[index])
.map((r) => r?.hits.map((hit) => hit && mapper(hit)).filter(nonNullable)) ?? null,
selectionSet: /* GraphQL */ `
{
results {
nbHits
hits {
... on AlgoliarecommendHit {
objectID
additionalProperties
try {
return (
(await context.algoliaRecommend.Query.algolia_getRecommendations({
key: { keyInput, objectId: atob(root.uid) },
argsFromKeys: (keys) => ({
input: {
requests: keys
.map(
(key) =>
({
[key.keyInput]: {
model: inputToModel[key.keyInput as string],
indexName: getIndexName(context),

...args,
objectID: key.objectId,
},
}) as unknown as AlgoliarecommendationsRequest_Input,
)
.filter(nonNullable),
},
}),
valuesFromResults: (res, keys) =>
keys
.map((_key, index) => res?.results[index])
.map((r) => r?.hits.map((hit) => hit && mapper(hit)).filter(nonNullable)) ?? null,
selectionSet: /* GraphQL */ `
{
results {
nbHits
hits {
... on AlgoliarecommendHit {
objectID
additionalProperties
}
}
}
}
}
`,
context,
info,
})) ?? null
)
`,
context,
info,
})) ?? null
)
} catch (e) {
if (isGraphQLError(e)) {
console.log(
'There was an error retrieving Algolia Recommendations, make sure the recommendation models are created',
e,
)
}

return null
}
}
24 changes: 14 additions & 10 deletions packages/algolia-recommend/mesh/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,72 +87,76 @@ type ProductResolver = ResolverFn<
>

if (isEnabled(import.meta.graphCommerce.algolia.relatedProducts)) {
const fieldName = enumToLocation(import.meta.graphCommerce.algolia.relatedProducts)
const resolve: ProductResolver = async (root, args, context, info) => {
const { objectID, threshold, fallbackParameters, maxRecommendations, queryParameters } =
await getRecommendationsArgs(root, args, context)

return getRecommendations(
const recommendations = await getRecommendations(
root,
'Related_products_Input',
{ objectID, threshold, fallbackParameters, maxRecommendations, queryParameters },
context,
info,
await createProductMapper(context),
)
return recommendations ?? root[fieldName] ?? null
}

productInterfaceTypes.forEach((productType) => {
if (!resolvers[productType]) resolvers[productType] = {}
resolvers[productType][enumToLocation(import.meta.graphCommerce.algolia.relatedProducts)] = {
resolvers[productType][fieldName] = {
selectionSet: `{ uid }`,
resolve,
}
})
}

if (isEnabled(import.meta.graphCommerce.algolia.lookingSimilar)) {
const fieldName = enumToLocation(import.meta.graphCommerce.algolia.lookingSimilar)
const resolve: ProductResolver = async (root, args, context, info) => {
const { objectID, threshold, fallbackParameters, maxRecommendations, queryParameters } =
await getRecommendationsArgs(root, args, context)
return getRecommendations(
const recommendations = await getRecommendations(
root,
'Looking_similar_Input',
{ objectID, threshold, fallbackParameters, maxRecommendations, queryParameters },
context,
info,
await createProductMapper(context),
)
return recommendations ?? root[fieldName] ?? null
}

productInterfaceTypes.forEach((productType) => {
if (!resolvers[productType]) resolvers[productType] = {}
resolvers[productType][enumToLocation(import.meta.graphCommerce.algolia.lookingSimilar)] = {
resolvers[productType][fieldName] = {
selectionSet: `{ uid }`,
resolve,
}
})
}

if (isEnabled(import.meta.graphCommerce.algolia.frequentlyBoughtTogether)) {
const resolver: ProductResolver = async (root, args, context, info) => {
const fieldName = enumToLocation(import.meta.graphCommerce.algolia.frequentlyBoughtTogether)

const resolve: ProductResolver = async (root, args, context, info) => {
const { objectID, threshold, maxRecommendations, queryParameters } =
await getRecommendationsArgs(root, args, context)

return getRecommendations(
const recommendations = await getRecommendations(
root,
'Frequently_bought_together_Input',
{ objectID, threshold, maxRecommendations, queryParameters },
context,
info,
await createProductMapper(context),
)
return recommendations ?? root[fieldName] ?? null
}

productInterfaceTypes.forEach((productType) => {
if (!resolvers[productType]) resolvers[productType] = {}
resolvers[productType][
enumToLocation(import.meta.graphCommerce.algolia.frequentlyBoughtTogether)
] = resolver
resolvers[productType][fieldName] = { selectionSet: `{ uid }`, resolve }
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ export function RecentlyViewedProducts(props: PluginProps<RecentlyViewedProducts
const ref = useRef<HTMLDivElement>(null)
const isInView = useInView(ref, { margin: '300px' })
const { skus } = useRecentlyViewedSkus({ exclude })
const productList = useRecentlyViewedProducts({ exclude, skip: !isInView && loading === 'lazy' })
const productList = useRecentlyViewedProducts({
exclude,
skip: skus.length === 0 || (!isInView && loading === 'lazy'),
})

if (
!import.meta.graphCommerce.recentlyViewedProducts?.enabled ||
Expand Down
4 changes: 2 additions & 2 deletions packages/graphql/components/InContextMask/InContextMask.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export type InContextMaskProps<

type InContextMaskContextType = { mask: boolean }

const InContextMaskContext = createContext<InContextMaskContextType | null>(null)
export const InContextMaskContext = createContext<InContextMaskContextType | undefined>(undefined)

export function useInContextInputMask() {
export function useInContextInputMask(): InContextMaskContextType {
const context = useContext(InContextMaskContext)

// eslint-disable-next-line react-hooks/rules-of-hooks
Expand Down
12 changes: 10 additions & 2 deletions packages/graphql/hooks/useInContextQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import type { InputMaybe, InContextInput } from '@graphcommerce/graphql-mesh'
import { useIsSSR } from '@graphcommerce/next-ui/hooks/useIsSsr'
// eslint-disable-next-line import/no-extraneous-dependencies
import { getCssFlag, removeCssFlag, setCssFlag } from '@graphcommerce/next-ui/utils/cssFlags'
import { useEffect } from 'react'
import { useContext, useEffect } from 'react'
import { QueryHookOptions, QueryResult, TypedDocumentNode, useQuery } from '../apollo'
import { InContextMaskContext } from '../components/InContextMask/InContextMask'
import { useInContextInput } from './useInContextInput'

/**
Expand Down Expand Up @@ -32,6 +33,8 @@ export function useInContextQuery<
const context = useInContextInput()
const isSsr = useIsSSR()

const inContext = useContext(InContextMaskContext)

useEffect(() => {
if (isSsr) return
if (context && !getCssFlag('in-context')) setCssFlag('in-context', true)
Expand All @@ -41,7 +44,7 @@ export function useInContextQuery<
const clientQuery = useQuery<Q, V>(document, {
...options,
variables: { ...options.variables, context } as V,
skip: skip && !context,
skip: !!inContext || (skip && !context),
})

let { data } = clientQuery
Expand All @@ -53,5 +56,10 @@ export function useInContextQuery<
mask = !skip ? !clientQuery.data && !clientQuery.previousData : !clientQuery.data
}

// If this method is called within an InContextMask, we skip this complete functionality so we show the parent mask.
if (inContext) {
mask = inContext.mask
}

return { ...clientQuery, data: data ?? unscopedResult, mask }
}
6 changes: 4 additions & 2 deletions packages/magento-customer/link/xMagentoCacheIdHeader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ export const xMagentoCacheIdHeader = new ApolloLink((operation, forward) => {
const { cache } = operation.getContext()
if (!cache) return data

const xMagentoCacheId = (data.extensions as { forwardedHeaders: Record<string, string> })
.forwardedHeaders['x-magento-cache-id']
const xMagentoCacheId = (
data.extensions as { forwardedHeaders: Record<string, string> } | undefined
)?.forwardedHeaders?.['x-magento-cache-id']

if (!xMagentoCacheId) return data

const tokenResult = cache.readQuery({ query: CustomerTokenDocument })
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useQuery } from '@graphcommerce/graphql'
import { extendableComponent } from '@graphcommerce/next-ui'
import { extendableComponent, ListFormat } from '@graphcommerce/next-ui'
import { Box } from '@mui/material'
import { ProductSpecsFragment } from './ProductSpecs.gql'
import { ProductSpecsTypesDocument } from './ProductSpecsTypes.gql'
Expand All @@ -24,19 +24,17 @@ export function ProductSpecsCustomAttributes(props: ProductSpecsCustomAttributes
{specs?.map((item) => (
<li key={item?.code}>
<div>
{
productSpecsTypes?.data?.attributesList?.items?.find(
(type) => type?.code === item?.code,
)?.label
}
{productSpecsTypes?.data?.attributesList?.items?.find(
(type) => type?.code === item?.code,
)?.label ?? item?.code}
</div>
<Box className={classes.options} sx={{ display: 'grid', gridAutoFlow: 'row' }}>
<Box className={classes.options}>
{item?.__typename === 'AttributeSelectedOptions' && (
<>
<ListFormat listStyle='long' type='unit'>
{item?.selected_options?.map((option) => (
<span key={option?.value}>{option?.label === '1' ? 'Yes' : option?.label}</span>
))}
</>
</ListFormat>
)}
{item?.__typename === 'AttributeValue' && <span key={item?.value}>{item.value}</span>}
</Box>
Expand Down
Loading

0 comments on commit 477c3d0

Please sign in to comment.