Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task/3948 #4001

Merged
merged 10 commits into from
Feb 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Optimized `translation.processor` to process only enabled locale CSV files - @pkarw (#3950)
- Remove commit register mapping - @gibkigonzo (#3875)
- Improved method `findConfigurableChildAsync` - find variant with lowest price - @gibkigonzo (#3939)
- Removed `product/loadConfigurableAttributes` calls - @andrzejewsky (#3336)
- Optimized attributes loading - @andrzejewsky (#3948)
- Improve typescript support for test utils - @resubaka (#4067)
- Removed `product/loadConfigurableAttributes` calls - @andrzejewsky, @gibkigonzo (#3336)

## [1.11.1] - 2020.02.05

Expand Down
7 changes: 4 additions & 3 deletions config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,15 +186,16 @@
"validSearchOptionsFromRouteParams": ["url-key", "slug", "id"]
},
"attribute": {
"includeFields": [ "activity", "attribute_code", "id", "entity_type_id", "options", "default_value", "is_user_defined", "frontend_label", "attribute_id", "default_frontend_label", "is_visible_on_front", "is_visible", "is_comparable", "tier_prices", "frontend_input" ]
"includeFields": [ "activity", "attribute_code", "id", "entity_type_id", "options", "default_value", "is_user_defined", "frontend_label", "attribute_id", "default_frontend_label", "is_visible_on_front", "is_visible", "is_comparable", "tier_prices", "frontend_input" ],
"loadByAttributeMetadata": false
},
"productList": {
"sort": "updated_at:desc",
"includeFields": [ "activity", "type_id", "*sku", "product_links", "tax_class_id", "special_price", "special_to_date", "special_from_date", "name", "price", "price_incl_tax", "original_price_incl_tax", "original_price", "special_price_incl_tax", "id", "image", "sale", "new", "url_path", "url_key", "status", "tier_prices", "configurable_children.sku", "configurable_children.price", "configurable_children.special_price", "configurable_children.price_incl_tax", "configurable_children.special_price_incl_tax", "configurable_children.original_price", "configurable_children.original_price_incl_tax", "*image","*small_image", "configurable_children.color", "configurable_children.size", "configurable_children.tier_prices", "final_price", "configurable_children.final_price"],
"includeFields": [ "attributes_metadata", "configurable_children.attributes", "activity", "type_id", "*sku", "product_links", "tax_class_id", "special_price", "special_to_date", "special_from_date", "name", "price", "price_incl_tax", "original_price_incl_tax", "original_price", "special_price_incl_tax", "id", "image", "sale", "new", "url_path", "url_key", "status", "tier_prices", "configurable_children.sku", "configurable_children.price", "configurable_children.special_price", "configurable_children.price_incl_tax", "configurable_children.special_price_incl_tax", "configurable_children.original_price", "configurable_children.original_price_incl_tax", "*image","*small_image", "configurable_children.color", "configurable_children.size", "configurable_children.tier_prices", "final_price", "configurable_children.final_price"],
"excludeFields": [ "attribute_set_id", "description", "configurable_options", "sgn", "*.sgn", "msrp_display_actual_price_type", "*.msrp_display_actual_price_type", "required_options", "media_gallery", "stock.use_config_min_qty", "stock.use_config_notify_stock_qty", "stock.stock_id", "stock.use_config_backorders", "stock.use_config_enable_qty_inc", "stock.enable_qty_increments", "stock.use_config_manage_stock", "stock.use_config_min_sale_qty", "stock.notify_stock_qty", "stock.use_config_max_sale_qty", "stock.use_config_max_sale_qty", "stock.qty_increments", "stock.stock_status_changed_auto", "stock.show_default_notification_message", "stock.use_config_qty_increments", "stock.is_decimal_divided"]
},
"productListWithChildren": {
"includeFields": [ "activity", "type_id", "sku", "name", "tax_class_id", "final_price", "special_price", "special_to_date", "special_from_date", "price", "price_incl_tax", "original_price_incl_tax", "original_price", "special_price_incl_tax", "id", "image", "sale", "new", "configurable_children.image", "configurable_children.sku", "configurable_children.price", "configurable_children.special_price", "configurable_children.price_incl_tax", "configurable_children.special_price_incl_tax", "configurable_children.original_price", "configurable_children.original_price_incl_tax", "configurable_children.color", "configurable_children.size", "configurable_children.id", "configurable_children.tier_prices", "product_links", "url_path", "url_key", "status", "tier_prices", "configurable_children.special_to_date", "configurable_children.special_from_date", "configurable_children.regular_price", "configurable_children.final_price"],
"includeFields": [ "attributes_metadata", "configurable_children.attributes", "activity", "type_id", "sku", "name", "tax_class_id", "final_price", "special_price", "special_to_date", "special_from_date", "price", "price_incl_tax", "original_price_incl_tax", "original_price", "special_price_incl_tax", "id", "image", "sale", "new", "configurable_children.image", "configurable_children.sku", "configurable_children.price", "configurable_children.special_price", "configurable_children.price_incl_tax", "configurable_children.special_price_incl_tax", "configurable_children.original_price", "configurable_children.original_price_incl_tax", "configurable_children.color", "configurable_children.size", "configurable_children.id", "configurable_children.tier_prices", "product_links", "url_path", "url_key", "status", "tier_prices", "configurable_children.special_to_date", "configurable_children.special_from_date", "configurable_children.regular_price", "configurable_children.final_price"],
"excludeFields": [ "attribute_set_id", "description", "sgn", "*.sgn", "msrp_display_actual_price_type", "*.msrp_display_actual_price_type", "required_options", "media_gallery", "stock.use_config_min_qty", "stock.use_config_notify_stock_qty", "stock.stock_id", "stock.use_config_backorders", "stock.use_config_enable_qty_inc", "stock.enable_qty_increments", "stock.use_config_manage_stock", "stock.use_config_min_sale_qty", "stock.notify_stock_qty", "stock.use_config_max_sale_qty", "stock.use_config_max_sale_qty", "stock.qty_increments", "stock.stock_status_changed_auto", "stock.show_default_notification_message", "stock.use_config_qty_increments", "stock.is_decimal_divided"]
},
"review": {
Expand Down
1 change: 1 addition & 0 deletions core/lib/search/adapter/api/searchAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export class SearchAdapter {
start: start,
perPage: size,
aggregations: resp.aggregations,
attributeMetadata: resp.attribute_metadata,
suggestions: resp.suggest
}
} else {
Expand Down
19 changes: 14 additions & 5 deletions core/modules/catalog-next/store/category/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,19 @@ const actions: ActionTree<CategoryState, RootState> = {
}
const searchQuery = getters.getCurrentFiltersFrom(route[products.routerFiltersSource], categoryMappedFilters)
let filterQr = buildFilterProductsQuery(searchCategory, searchQuery.filters)
const {items, perPage, start, total, aggregations} = await quickSearchByQuery({
const {items, perPage, start, total, aggregations, attributeMetadata} = await quickSearchByQuery({
query: filterQr,
sort: searchQuery.sort || `${products.defaultSortBy.attribute}:${products.defaultSortBy.order}`,
includeFields: entities.productList.includeFields,
excludeFields: entities.productList.excludeFields,
size: pageSize
})
await dispatch('loadAvailableFiltersFrom', {aggregations, category: searchCategory, filters: searchQuery.filters})
await dispatch('loadAvailableFiltersFrom', {
aggregations,
attributeMetadata,
category: searchCategory,
filters: searchQuery.filters
})
commit(types.CATEGORY_SET_SEARCH_PRODUCTS_STATS, { perPage, start, total })
const configuredProducts = await dispatch('processCategoryProducts', { products: items, filters: searchQuery.filters })
commit(types.CATEGORY_SET_PRODUCTS, configuredProducts)
Expand Down Expand Up @@ -174,14 +179,17 @@ const actions: ActionTree<CategoryState, RootState> = {
async loadCategoryFilters ({ dispatch, getters }, category) {
const searchCategory = category || getters.getCurrentCategory
let filterQr = buildFilterProductsQuery(searchCategory)
const {aggregations} = await quickSearchByQuery({
const { aggregations, attributeMetadata } = await quickSearchByQuery({
query: filterQr,
size: config.products.maxFiltersQuerySize,
excludeFields: ['*']
})
await dispatch('loadAvailableFiltersFrom', {aggregations, category})
await dispatch('loadAvailableFiltersFrom', { aggregations, attributeMetadata: attributeMetadata, category })
},
async loadAvailableFiltersFrom ({ commit, getters }, {aggregations, category, filters = {}}) {
async loadAvailableFiltersFrom ({ commit, getters, dispatch }, {aggregations, attributeMetadata, category, filters = {}}) {
if (config.entities.attribute.loadByAttributeMetadata) {
await dispatch('attribute/loadCategoryAttributes', { attributeMetadata }, { root: true })
}
const aggregationFilters = getters.getAvailableFiltersFrom(aggregations)
const currentCategory = category || getters.getCurrentCategory
const categoryMappedFilters = getters.getFiltersMap[currentCategory.id]
Expand All @@ -192,6 +200,7 @@ const actions: ActionTree<CategoryState, RootState> = {
}
commit(types.CATEGORY_SET_CATEGORY_FILTERS, {category, filters: resultFilters})
},

async switchSearchFilters ({ dispatch }, filterVariants: FilterVariant[] = []) {
let currentQuery = router.currentRoute[products.routerFiltersSource]
filterVariants.forEach(filterVariant => {
Expand Down
34 changes: 34 additions & 0 deletions core/modules/catalog/helpers/transformMetadataToAttributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import uniqBy from 'lodash-es/uniqBy'

const transformMetadataToAttributes = (attributeMetadata) => attributeMetadata
.reduce((prev, curr) => ([ ...prev, ...curr ]), [])
.reduce((prev, curr) => {
const attribute = prev.find(a => a.attribute_id === curr.attribute_id && a.options)

if (attribute) {
return prev.map(attr => {
if (attr.attribute_id === curr.attribute_id) {
return {
...attr,
options: uniqBy([...attr.options, ...curr.options], (obj) => `${obj.label}_${obj.value}`)
}
}

return attr
})
}

return [...prev, curr]
}, [])
.reduce((prev, curr) => ({
attrHashByCode: {
...(prev.attrHashByCode || {}),
[curr.attribute_code]: curr
},
attrHashById: {
...(prev.attrHashById || {}),
[curr.attribute_id]: curr
}
}), { attrHashByCode: {}, attrHashById: {} })

export default transformMetadataToAttributes
8 changes: 5 additions & 3 deletions core/modules/catalog/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ export const CatalogModule: StorefrontModule = async function ({store, router, a
store.registerModule('tax', taxModule)
store.registerModule('category', categoryModule)

await store.dispatch('attribute/list', { // loading attributes for application use
filterValues: uniq([...config.products.defaultFilters, ...config.entities.productListWithChildren.includeFields])
})
if (!config.entities.attribute.loadByAttributeMetadata) {
await store.dispatch('attribute/list', { // loading attributes for application use
filterValues: uniq([...config.products.defaultFilters, ...config.entities.productListWithChildren.includeFields])
})
}

if (!isServer) {
// Things moved from Product.js
Expand Down
26 changes: 21 additions & 5 deletions core/modules/catalog/store/attribute/actions.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import * as types from './mutation-types'
import { quickSearchByQuery } from '@vue-storefront/core/lib/search'
import { StorageManager } from '@vue-storefront/core/lib/storage-manager'
import AttributeState from '../../types/AttributeState'
import AttributeState from '@vue-storefront/core/modules/catalog/types/AttributeState'
import RootState from '@vue-storefront/core/types/RootState'
import { ActionTree } from 'vuex'
import config from 'config'
import { Logger } from '@vue-storefront/core/lib/logger'
import { entityKeyName } from '@vue-storefront/core/lib/store/entities'
import { prefetchCachedAttributes } from '../../helpers/prefetchCachedAttributes'
import createAttributesListQuery from './../../helpers/createAttributesListQuery'
import reduceAttributesLists from './../../helpers/reduceAttributesLists'
import filterAttributes from '../../helpers/filterAttributes'
import { prefetchCachedAttributes } from '@vue-storefront/core/modules/catalog/helpers/prefetchCachedAttributes'
import createAttributesListQuery from '@vue-storefront/core/modules/catalog/helpers/createAttributesListQuery'
import reduceAttributesLists from '@vue-storefront/core/modules/catalog/helpers/reduceAttributesLists'
import transformMetadataToAttributes from '@vue-storefront/core/modules/catalog/helpers/transformMetadataToAttributes'
import filterAttributes from '@vue-storefront/core/modules/catalog/helpers/filterAttributes'

const actions: ActionTree<AttributeState, RootState> = {
async updateAttributes ({ commit, getters }, { attributes }) {
Expand Down Expand Up @@ -82,6 +83,21 @@ const actions: ActionTree<AttributeState, RootState> = {
await dispatch('updateAttributes', { attributes })

return resp
},
async loadProductAttributes (context, { products }) {
const attributeMetadata = products
.filter(product => product.attributes_metadata)
.map(product => product.attributes_metadata)

const attributes = transformMetadataToAttributes(attributeMetadata)

context.commit(types.ATTRIBUTE_UPD_ATTRIBUTES, attributes)
},
async loadCategoryAttributes (context, { attributeMetadata }) {
if (!attributeMetadata) return
const attributes = transformMetadataToAttributes([attributeMetadata])

context.commit(types.ATTRIBUTE_UPD_ATTRIBUTES, attributes)
}
}

Expand Down
16 changes: 12 additions & 4 deletions core/modules/catalog/store/product/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,13 @@ const actions: ActionTree<ProductState, RootState> = {
/**
* Setup product current variants
*/
setupVariants (context, { product }) {
async setupVariants (context, { product }) {
if (product.type_id !== 'configurable' || !product.hasOwnProperty('configurable_options')) {
return
}

if (config.entities.attribute.loadByAttributeMetadata) {
await context.dispatch('attribute/loadProductAttributes', { products: [product] }, { root: true })
}
let productOptions = {}
for (let option of product.configurable_options) {
for (let ov of option.values) {
Expand All @@ -267,7 +269,8 @@ const actions: ActionTree<ProductState, RootState> = {
}
}
context.commit(types.PRODUCT_SET_CURRENT_OPTIONS, productOptions)
populateProductConfigurationAsync(context, { selectedVariant: context.getters.getCurrentProduct, product: product })
let selectedVariant = context.getters.getCurrentProduct
populateProductConfigurationAsync(context, { selectedVariant: selectedVariant, product: product })
},
filterUnavailableVariants (context, { product }) {
return filterOutUnavailableVariants(context, product)
Expand Down Expand Up @@ -634,7 +637,12 @@ const actions: ActionTree<ProductState, RootState> = {
throw new Error(`Product query returned empty result product visibility = ${product.visibility}`)
}

await dispatch('loadProductAttributes', { product })
if (config.entities.attribute.loadByAttributeMetadata) {
await dispatch('attribute/loadProductAttributes', { products: [product] }, { root: true })
} else {
await dispatch('loadProductAttributes', { product })
}

const syncPromises = []
const variantsFilter = dispatch('filterUnavailableVariants', { product })
const gallerySetup = dispatch('setProductGallery', { product })
Expand Down
11 changes: 7 additions & 4 deletions core/modules/compare/components/Compare.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { mapGetters } from 'vuex'
import Product from '@vue-storefront/core/modules/catalog/types/Product'
import compareMountedMixin from '@vue-storefront/core/modules/compare/mixins/compareMountedMixin'
import config from 'config'

export const Compare = {
name: 'Compare',
Expand All @@ -12,10 +13,12 @@ export const Compare = {
})
},
created () {
this.$store.dispatch('attribute/list', {
filterValues: [],
filterField: 'is_user_defined'
})
if (!config.entities.attribute.loadByAttributeMetadata) {
this.$store.dispatch('attribute/list', {
filterValues: [],
filterField: 'is_user_defined'
})
}
},
methods: {
removeFromCompare (product: Product) {
Expand Down
22 changes: 16 additions & 6 deletions core/modules/compare/store/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,26 @@ import RootState from '@vue-storefront/core/types/RootState'
import CompareState from '../types/CompareState'
import { StorageManager } from '@vue-storefront/core/lib/storage-manager'
import { Logger } from '@vue-storefront/core/lib/logger'
import config from 'config'

const actions: ActionTree<CompareState, RootState> = {
async load ({ commit, getters, dispatch }, force: boolean = false) {
if (!force && getters.isCompareLoaded) return
commit(types.SET_COMPARE_LOADED)
const storedItems = await dispatch('fetchCurrentCompare')
if (force || !getters.isCompareLoaded) {
commit(types.SET_COMPARE_LOADED)
const storedItems = await dispatch('fetchCurrentCompare')

if (storedItems) {
commit(types.COMPARE_LOAD_COMPARE, storedItems)
Logger.info('Compare state loaded from browser cache: ', 'cache', storedItems)()
if (storedItems) {
commit(types.COMPARE_LOAD_COMPARE, storedItems)
Logger.info('Compare state loaded from browser cache: ', 'cache', storedItems)()
}

if (config.entities.attribute.loadByAttributeMetadata) {
dispatch(
'attribute/loadProductAttributes',
{ products: getters.getCompareItems },
{ root: true }
)
}
}
},
async fetchCurrentCompare () {
Expand Down
20 changes: 0 additions & 20 deletions core/modules/compare/test/unit/components/Compare.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,6 @@ jest.mock('@vue-storefront/core/helpers', () => ({
jest.mock('@vue-storefront/core/modules/compare/mixins/compareMountedMixin', () => ({}))

describe('Compare', () => {
it('Compare dispatches attribute list action on created', () => {
const storeMock = {
modules: {
attribute: {
actions: {
list: jest.fn(() => [])
},
namespaced: true
}
}
};

mountMixinWithStore(Compare, storeMock);

expect(storeMock.modules.attribute.actions.list).toBeCalledWith(expect.anything(), {
filterValues: [],
filterField: 'is_user_defined'
}, undefined);
})

it('removeFromCompare dispatches addItem action', () => {
const product = {};

Expand Down
5 changes: 0 additions & 5 deletions core/pages/Category.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Vue from 'vue'
import toString from 'lodash-es/toString'
import config from 'config'

import i18n from '@vue-storefront/i18n'
import store from '@vue-storefront/core/store'
import EventBus from '@vue-storefront/core/compatibility/plugins/event-bus'
Expand Down Expand Up @@ -84,10 +83,6 @@ export default {
const defaultFilters = config.products.defaultFilters
store.dispatch('category/resetFilters')
EventBus.$emit('filter-reset')
await store.dispatch('attribute/list', { // load filter attributes for this specific category
filterValues: defaultFilters, // TODO: assign specific filters/ attribute codes dynamicaly to specific categories
includeFields: config.entities.optimize && isServer ? config.entities.attribute.includeFields : null
})
const parentCategory = await store.dispatch('category/single', { key: config.products.useMagentoUrlKeys ? 'url_key' : 'slug', value: route.params.slug })
let query = store.getters['category/getCurrentCategoryProductQuery']
if (!query.searchProductQuery) {
Expand Down
3 changes: 2 additions & 1 deletion core/types/search/SearchResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export interface SearchResponse {
offline?: boolean,
cache?: boolean,
noresults?: boolean,
suggestions: any
suggestions: any,
attributeMetadata?: any
}
Loading