Skip to content

Commit

Permalink
feat(core): Collections can control inheritance of filters
Browse files Browse the repository at this point in the history
Relates to #1382. This commit introduces a new property on the Collection entity & GraphQL type -
`inheritFilters`. When set to `true` (the default), behaviour is the same as before - a Collection's
filters will be composed of its own filters, plus those of all ancestors up to the root.

Setting to `false` allows you to nest a Collection and set up filters which act completely
independently of the parent filters. This allows much more flexibility in setting up category
hierarchies.

BREAKING CHANGE: The new `inheritFilters` property on the Collection entity will require a DB
migration.
  • Loading branch information
michaelbromley committed Mar 3, 2022
1 parent 8e0a2f5 commit 5d4206f
Show file tree
Hide file tree
Showing 12 changed files with 212 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ export type Collection = Node & {
featuredAsset?: Maybe<Asset>;
filters: Array<ConfigurableOperation>;
id: Scalars['ID'];
inheritFilters: Scalars['Boolean'];
isPrivate: Scalars['Boolean'];
languageCode?: Maybe<LanguageCode>;
name: Scalars['String'];
Expand All @@ -381,6 +382,7 @@ export type CollectionFilterParameter = {
createdAt?: InputMaybe<DateOperators>;
description?: InputMaybe<StringOperators>;
id?: InputMaybe<IdOperators>;
inheritFilters?: InputMaybe<BooleanOperators>;
isPrivate?: InputMaybe<BooleanOperators>;
languageCode?: InputMaybe<StringOperators>;
name?: InputMaybe<StringOperators>;
Expand Down Expand Up @@ -597,6 +599,7 @@ export type CreateCollectionInput = {
customFields?: InputMaybe<Scalars['JSON']>;
featuredAssetId?: InputMaybe<Scalars['ID']>;
filters: Array<ConfigurableOperationInput>;
inheritFilters?: InputMaybe<Scalars['Boolean']>;
isPrivate?: InputMaybe<Scalars['Boolean']>;
parentId?: InputMaybe<Scalars['ID']>;
translations: Array<CreateCollectionTranslationInput>;
Expand Down Expand Up @@ -4734,6 +4737,7 @@ export type UpdateCollectionInput = {
featuredAssetId?: InputMaybe<Scalars['ID']>;
filters?: InputMaybe<Array<ConfigurableOperationInput>>;
id: Scalars['ID'];
inheritFilters?: InputMaybe<Scalars['Boolean']>;
isPrivate?: InputMaybe<Scalars['Boolean']>;
parentId?: InputMaybe<Scalars['ID']>;
translations?: InputMaybe<Array<UpdateCollectionTranslationInput>>;
Expand Down
4 changes: 4 additions & 0 deletions packages/common/src/generated-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ export type Collection = Node & {
featuredAsset?: Maybe<Asset>;
filters: Array<ConfigurableOperation>;
id: Scalars['ID'];
inheritFilters: Scalars['Boolean'];
isPrivate: Scalars['Boolean'];
languageCode?: Maybe<LanguageCode>;
name: Scalars['String'];
Expand Down Expand Up @@ -383,6 +384,7 @@ export type CollectionFilterParameter = {
createdAt?: InputMaybe<DateOperators>;
description?: InputMaybe<StringOperators>;
id?: InputMaybe<IdOperators>;
inheritFilters?: InputMaybe<BooleanOperators>;
isPrivate?: InputMaybe<BooleanOperators>;
languageCode?: InputMaybe<StringOperators>;
name?: InputMaybe<StringOperators>;
Expand Down Expand Up @@ -610,6 +612,7 @@ export type CreateCollectionInput = {
customFields?: InputMaybe<Scalars['JSON']>;
featuredAssetId?: InputMaybe<Scalars['ID']>;
filters: Array<ConfigurableOperationInput>;
inheritFilters?: InputMaybe<Scalars['Boolean']>;
isPrivate?: InputMaybe<Scalars['Boolean']>;
parentId?: InputMaybe<Scalars['ID']>;
translations: Array<CreateCollectionTranslationInput>;
Expand Down Expand Up @@ -4989,6 +4992,7 @@ export type UpdateCollectionInput = {
featuredAssetId?: InputMaybe<Scalars['ID']>;
filters?: InputMaybe<Array<ConfigurableOperationInput>>;
id: Scalars['ID'];
inheritFilters?: InputMaybe<Scalars['Boolean']>;
isPrivate?: InputMaybe<Scalars['Boolean']>;
parentId?: InputMaybe<Scalars['ID']>;
translations?: InputMaybe<Array<UpdateCollectionTranslationInput>>;
Expand Down
188 changes: 140 additions & 48 deletions packages/core/e2e/collection.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1405,6 +1405,7 @@ describe('Collection resolver', () => {
'USB Cable',
'Tripod',
'Hat',
'Boots',
]);
});

Expand Down Expand Up @@ -1566,59 +1567,150 @@ describe('Collection resolver', () => {
});
});

it('filter inheritance of nested collections (issue #158)', async () => {
const { createCollection: pearElectronics } = await adminClient.query<
Codegen.CreateCollectionSelectVariantsMutation,
Codegen.CreateCollectionSelectVariantsMutationVariables
>(CREATE_COLLECTION_SELECT_VARIANTS, {
input: {
parentId: electronicsCollection.id,
translations: [
{
languageCode: LanguageCode.en,
name: 'pear electronics',
description: '',
slug: 'pear-electronics',
},
],
filters: [
{
code: facetValueCollectionFilter.code,
arguments: [
{
name: 'facetValueIds',
value: `["${getFacetValueId('pear')}"]`,
},
{
name: 'containsAny',
value: `false`,
},
],
},
],
} as CreateCollectionInput,
describe('filter inheritance', () => {
let clothesCollectionId: string;

it('filter inheritance of nested collections (issue #158)', async () => {
const { createCollection: pearElectronics } = await adminClient.query<
Codegen.CreateCollectionSelectVariantsMutation,
Codegen.CreateCollectionSelectVariantsMutationVariables
>(CREATE_COLLECTION_SELECT_VARIANTS, {
input: {
parentId: electronicsCollection.id,
translations: [
{
languageCode: LanguageCode.en,
name: 'pear electronics',
description: '',
slug: 'pear-electronics',
},
],
filters: [
{
code: facetValueCollectionFilter.code,
arguments: [
{
name: 'facetValueIds',
value: `["${getFacetValueId('pear')}"]`,
},
{
name: 'containsAny',
value: `false`,
},
],
},
],
} as CreateCollectionInput,
});

await awaitRunningJobs(adminClient, 5000);

const result = await adminClient.query<
Codegen.GetCollectionProductsQuery,
Codegen.GetCollectionProductsQueryVariables
>(GET_COLLECTION_PRODUCT_VARIANTS, { id: pearElectronics.id });
expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
'Laptop 13 inch 8GB',
'Laptop 15 inch 8GB',
'Laptop 13 inch 16GB',
'Laptop 15 inch 16GB',
'Curvy Monitor 24 inch',
'Curvy Monitor 27 inch',
'Gaming PC i7-8700 240GB SSD',
'Instant Camera',
// no "Hat"
]);
});

await awaitRunningJobs(adminClient, 5000);
it('child collection with no inheritance', async () => {
const { createCollection: clothesCollection } = await adminClient.query<
Codegen.CreateCollectionSelectVariantsMutation,
Codegen.CreateCollectionSelectVariantsMutationVariables
>(CREATE_COLLECTION_SELECT_VARIANTS, {
input: {
parentId: electronicsCollection.id,
translations: [
{
languageCode: LanguageCode.en,
name: 'clothes',
description: '',
slug: 'clothes',
},
],
inheritFilters: false,
filters: [
{
code: facetValueCollectionFilter.code,
arguments: [
{
name: 'facetValueIds',
value: `["${getFacetValueId('clothing')}"]`,
},
{
name: 'containsAny',
value: `false`,
},
],
},
],
} as CreateCollectionInput,
});

const result = await adminClient.query<
Codegen.GetCollectionProductsQuery,
Codegen.GetCollectionProductsQueryVariables
>(GET_COLLECTION_PRODUCT_VARIANTS, { id: pearElectronics.id });
expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
'Laptop 13 inch 8GB',
'Laptop 15 inch 8GB',
'Laptop 13 inch 16GB',
'Laptop 15 inch 16GB',
'Curvy Monitor 24 inch',
'Curvy Monitor 27 inch',
'Gaming PC i7-8700 240GB SSD',
'Instant Camera',
// no "Hat"
]);
await awaitRunningJobs(adminClient, 5000);

clothesCollectionId = clothesCollection.id;

const result = await adminClient.query<
Codegen.GetCollectionProductsQuery,
Codegen.GetCollectionProductsQueryVariables
>(GET_COLLECTION_PRODUCT_VARIANTS, { id: clothesCollection.id });
expect(result.collection!.productVariants.items.map(i => i.name)).toEqual(['Hat', 'Boots']);
});

it('grandchild collection with inheritance (root -> no inherit -> inherit', async () => {
const { createCollection: footwearCollection } = await adminClient.query<
Codegen.CreateCollectionSelectVariantsMutation,
Codegen.CreateCollectionSelectVariantsMutationVariables
>(CREATE_COLLECTION_SELECT_VARIANTS, {
input: {
parentId: clothesCollectionId,
translations: [
{
languageCode: LanguageCode.en,
name: 'footwear',
description: '',
slug: 'footwear',
},
],
inheritFilters: true,
filters: [
{
code: facetValueCollectionFilter.code,
arguments: [
{
name: 'facetValueIds',
value: `["${getFacetValueId('footwear')}"]`,
},
{
name: 'containsAny',
value: `false`,
},
],
},
],
} as CreateCollectionInput,
});

await awaitRunningJobs(adminClient, 5000);

const result = await adminClient.query<
Codegen.GetCollectionProductsQuery,
Codegen.GetCollectionProductsQueryVariables
>(GET_COLLECTION_PRODUCT_VARIANTS, { id: footwearCollection.id });
expect(result.collection!.productVariants.items.map(i => i.name)).toEqual(['Boots']);
});
});
});

describe('Product collections property', () => {
it('returns all collections to which the Product belongs', async () => {
const result = await adminClient.query<
Expand Down
Loading

0 comments on commit 5d4206f

Please sign in to comment.