From ed7e110188a9f71c5074158ae56af74ca5fddc95 Mon Sep 17 00:00:00 2001 From: tniezg Date: Wed, 22 Sep 2021 14:30:00 +0200 Subject: [PATCH] Allow filtering variant images by option type --- framework/spree/.env.template | 1 + .../spree/errors/MissingOptionTypeError.ts | 1 + .../spree/errors/MissingOptionValueError.ts | 1 + framework/spree/isomorphic-config.ts | 13 +- framework/spree/utils/normalize-product.ts | 132 +++++++++++++++--- .../validate-all-products-taxonomy-id.ts | 0 .../validate-cookie-expire.ts | 0 .../validate-images-option-filter.ts | 15 ++ .../validate-placeholder-image-url.ts | 0 .../validate-products-prerender-count.ts | 0 10 files changed, 141 insertions(+), 22 deletions(-) create mode 100644 framework/spree/errors/MissingOptionTypeError.ts create mode 100644 framework/spree/errors/MissingOptionValueError.ts rename framework/spree/utils/{ => validations}/validate-all-products-taxonomy-id.ts (100%) rename framework/spree/utils/{ => validations}/validate-cookie-expire.ts (100%) create mode 100644 framework/spree/utils/validations/validate-images-option-filter.ts rename framework/spree/utils/{ => validations}/validate-placeholder-image-url.ts (100%) rename framework/spree/utils/{ => validations}/validate-products-prerender-count.ts (100%) diff --git a/framework/spree/.env.template b/framework/spree/.env.template index 5c4f05184..4fd558569 100644 --- a/framework/spree/.env.template +++ b/framework/spree/.env.template @@ -17,3 +17,4 @@ NEXT_PUBLIC_SPREE_SHOW_SINGLE_VARIANT_OPTIONS=false NEXT_PUBLIC_SPREE_LAST_UPDATED_PRODUCTS_PRERENDER_COUNT=10 NEXT_PUBLIC_SPREE_PRODUCT_PLACEHOLDER_IMAGE_URL=/product-img-placeholder.svg NEXT_PUBLIC_SPREE_LINE_ITEM_PLACEHOLDER_IMAGE_URL=/product-img-placeholder.svg +NEXT_PUBLIC_SPREE_IMAGES_OPTION_FILTER=false diff --git a/framework/spree/errors/MissingOptionTypeError.ts b/framework/spree/errors/MissingOptionTypeError.ts new file mode 100644 index 000000000..14c51aeca --- /dev/null +++ b/framework/spree/errors/MissingOptionTypeError.ts @@ -0,0 +1 @@ +export default class MissingOptionTypeError extends Error {} diff --git a/framework/spree/errors/MissingOptionValueError.ts b/framework/spree/errors/MissingOptionValueError.ts new file mode 100644 index 000000000..04457ac5e --- /dev/null +++ b/framework/spree/errors/MissingOptionValueError.ts @@ -0,0 +1 @@ +export default class MissingOptionValueError extends Error {} diff --git a/framework/spree/isomorphic-config.ts b/framework/spree/isomorphic-config.ts index ed1bb4a55..9e7745291 100644 --- a/framework/spree/isomorphic-config.ts +++ b/framework/spree/isomorphic-config.ts @@ -1,9 +1,10 @@ import forceIsomorphicConfigValues from './utils/force-isomorphic-config-values' import requireConfig from './utils/require-config' -import validateAllProductsTaxonomyId from './utils/validate-all-products-taxonomy-id' -import validateCookieExpire from './utils/validate-cookie-expire' -import validatePlaceholderImageUrl from './utils/validate-placeholder-image-url' -import validateProductsPrerenderCount from './utils/validate-products-prerender-count' +import validateAllProductsTaxonomyId from './utils/validations/validate-all-products-taxonomy-id' +import validateCookieExpire from './utils/validations/validate-cookie-expire' +import validateImagesOptionFilter from './utils/validations/validate-images-option-filter' +import validatePlaceholderImageUrl from './utils/validations/validate-placeholder-image-url' +import validateProductsPrerenderCount from './utils/validations/validate-products-prerender-count' const isomorphicConfig = { apiHost: process.env.NEXT_PUBLIC_SPREE_API_HOST, @@ -31,6 +32,9 @@ const isomorphicConfig = { lineItemPlaceholderImageUrl: validatePlaceholderImageUrl( process.env.NEXT_PUBLIC_SPREE_LINE_ITEM_PLACEHOLDER_IMAGE_URL ), + imagesOptionFilter: validateImagesOptionFilter( + process.env.NEXT_PUBLIC_SPREE_IMAGES_OPTION_FILTER + ), } export default forceIsomorphicConfigValues( @@ -49,6 +53,7 @@ export default forceIsomorphicConfigValues( 'lastUpdatedProductsPrerenderCount', 'productPlaceholderImageUrl', 'lineItemPlaceholderImageUrl', + 'imagesOptionFilter', ] ) diff --git a/framework/spree/utils/normalize-product.ts b/framework/spree/utils/normalize-product.ts index 6ef53f8a5..07205cd7f 100644 --- a/framework/spree/utils/normalize-product.ts +++ b/framework/spree/utils/normalize-product.ts @@ -13,13 +13,20 @@ import expandOptions from './expand-options' import getMediaGallery from './get-media-gallery' import getProductPath from './get-product-path' import MissingPrimaryVariantError from '../errors/MissingPrimaryVariantError' +import MissingOptionTypeError from '../errors/MissingOptionTypeError' +import MissingOptionValueError from '../errors/MissingOptionValueError' import type { SpreeSdkResponse, VariantAttr } from '@framework/types' import { jsonApi } from '@spree/storefront-api-v2-sdk' +import { JsonApiDocument } from '@spree/storefront-api-v2-sdk/types/interfaces/JsonApi' const placeholderImage = requireConfigValue('productPlaceholderImageUrl') as | string | false +const imagesOptionFilter = requireConfigValue('imagesOptionFilter') as + | string + | false + const normalizeProduct = ( spreeSuccessResponse: SpreeSdkResponse, spreeProduct: ProductAttr @@ -38,24 +45,6 @@ const normalizeProduct = ( const sku = primaryVariant.attributes.sku - const spreeImageRecords = jsonApi.findRelationshipDocuments( - spreeSuccessResponse, - spreeProduct, - 'images' - ) - - const productImages = getMediaGallery( - spreeImageRecords, - createGetAbsoluteImageUrl(requireConfigValue('imageHost') as string) - ) - - const images: ProductImage[] = - productImages.length === 0 - ? placeholderImage === false - ? [] - : [{ url: placeholderImage }] - : productImages - const price: ProductPrice = { value: parseFloat(spreeProduct.attributes.price), currencyCode: spreeProduct.attributes.currency, @@ -106,6 +95,113 @@ const normalizeProduct = ( } }) + const spreePrimaryVariantImageRecords = jsonApi.findRelationshipDocuments( + spreeSuccessResponse, + primaryVariant, + 'images' + ) + + let spreeVariantImageRecords: JsonApiDocument[] + + if (imagesOptionFilter === false) { + spreeVariantImageRecords = spreeVariantRecords.reduce( + (accumulatedImageRecords, spreeVariantRecord) => { + return [ + ...accumulatedImageRecords, + ...jsonApi.findRelationshipDocuments( + spreeSuccessResponse, + spreeVariantRecord, + 'images' + ), + ] + }, + [] + ) + } else { + const spreeOptionTypes = jsonApi.findRelationshipDocuments( + spreeSuccessResponse, + spreeProduct, + 'option_types' + ) + + const imagesFilterOptionType = spreeOptionTypes.find( + (spreeOptionType) => + spreeOptionType.attributes.name === imagesOptionFilter + ) + + if (!imagesFilterOptionType) { + throw new MissingOptionTypeError( + `Couldn't find option type having name ${imagesOptionFilter}.` + ) + } + + const imagesOptionTypeFilterId = imagesFilterOptionType.id + const includedOptionValuesImagesIds: string[] = [] + + spreeVariantImageRecords = spreeVariantRecords.reduce( + (accumulatedImageRecords, spreeVariantRecord) => { + const spreeVariantOptionValuesIdentifiers: RelationType[] = + spreeVariantRecord.relationships.option_values.data + + const spreeOptionValueOfFilterTypeIdentifier = + spreeVariantOptionValuesIdentifiers.find( + (spreeVariantOptionValuesIdentifier: RelationType) => + imagesFilterOptionType.relationships.option_values.data.some( + (filterOptionTypeValueIdentifier: RelationType) => + filterOptionTypeValueIdentifier.id === + spreeVariantOptionValuesIdentifier.id + ) + ) + + if (!spreeOptionValueOfFilterTypeIdentifier) { + throw new MissingOptionValueError( + `Couldn't find option value related to option type with id ${imagesOptionTypeFilterId}.` + ) + } + + const optionValueImagesAlreadyIncluded = + includedOptionValuesImagesIds.includes( + spreeOptionValueOfFilterTypeIdentifier.id + ) + + if (optionValueImagesAlreadyIncluded) { + return accumulatedImageRecords + } + + includedOptionValuesImagesIds.push( + spreeOptionValueOfFilterTypeIdentifier.id + ) + + return [ + ...accumulatedImageRecords, + ...jsonApi.findRelationshipDocuments( + spreeSuccessResponse, + spreeVariantRecord, + 'images' + ), + ] + }, + [] + ) + } + + const spreeImageRecords = [ + ...spreePrimaryVariantImageRecords, + ...spreeVariantImageRecords, + ] + + const productImages = getMediaGallery( + spreeImageRecords, + createGetAbsoluteImageUrl(requireConfigValue('imageHost') as string) + ) + + const images: ProductImage[] = + productImages.length === 0 + ? placeholderImage === false + ? [] + : [{ url: placeholderImage }] + : productImages + const slug = spreeProduct.attributes.slug const path = getProductPath(spreeProduct) diff --git a/framework/spree/utils/validate-all-products-taxonomy-id.ts b/framework/spree/utils/validations/validate-all-products-taxonomy-id.ts similarity index 100% rename from framework/spree/utils/validate-all-products-taxonomy-id.ts rename to framework/spree/utils/validations/validate-all-products-taxonomy-id.ts diff --git a/framework/spree/utils/validate-cookie-expire.ts b/framework/spree/utils/validations/validate-cookie-expire.ts similarity index 100% rename from framework/spree/utils/validate-cookie-expire.ts rename to framework/spree/utils/validations/validate-cookie-expire.ts diff --git a/framework/spree/utils/validations/validate-images-option-filter.ts b/framework/spree/utils/validations/validate-images-option-filter.ts new file mode 100644 index 000000000..8b6ef9892 --- /dev/null +++ b/framework/spree/utils/validations/validate-images-option-filter.ts @@ -0,0 +1,15 @@ +const validateImagesOptionFilter = ( + optionTypeNameOrFalse: unknown +): string | false => { + if (!optionTypeNameOrFalse || optionTypeNameOrFalse === 'false') { + return false + } + + if (typeof optionTypeNameOrFalse === 'string') { + return optionTypeNameOrFalse + } + + throw new TypeError('optionTypeNameOrFalse must be a string or falsy.') +} + +export default validateImagesOptionFilter diff --git a/framework/spree/utils/validate-placeholder-image-url.ts b/framework/spree/utils/validations/validate-placeholder-image-url.ts similarity index 100% rename from framework/spree/utils/validate-placeholder-image-url.ts rename to framework/spree/utils/validations/validate-placeholder-image-url.ts diff --git a/framework/spree/utils/validate-products-prerender-count.ts b/framework/spree/utils/validations/validate-products-prerender-count.ts similarity index 100% rename from framework/spree/utils/validate-products-prerender-count.ts rename to framework/spree/utils/validations/validate-products-prerender-count.ts