Skip to content

Commit

Permalink
feat(core): Implement CollectionFilter based on ProductVariant name
Browse files Browse the repository at this point in the history
Relates to #71
  • Loading branch information
michaelbromley committed Apr 25, 2019
1 parent 94bd276 commit 18549c7
Show file tree
Hide file tree
Showing 11 changed files with 323 additions and 177 deletions.
3 changes: 2 additions & 1 deletion packages/common/src/generated-shop-types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// tslint:disable
// Generated in 2019-04-24T20:30:12+02:00
// Generated in 2019-04-25T09:11:09+02:00
export type Maybe<T> = T | null;

export interface OrderListOptions {
Expand Down Expand Up @@ -738,6 +738,7 @@ export enum ConfigArgType {
DATETIME = 'DATETIME',
BOOLEAN = 'BOOLEAN',
FACET_VALUE_IDS = 'FACET_VALUE_IDS',
STRING_OPERATOR = 'STRING_OPERATOR',
}
/** Permissions for administrators and customers */
export enum Permission {
Expand Down
67 changes: 34 additions & 33 deletions packages/common/src/generated-types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// tslint:disable
// Generated in 2019-04-24T20:30:13+02:00
// Generated in 2019-04-25T09:11:10+02:00
export type Maybe<T> = T | null;


Expand Down Expand Up @@ -1169,6 +1169,18 @@ export interface UpdateRoleInput {
permissions?: Maybe<Permission[]>;
}

export interface CreateTaxCategoryInput {

name: string;
}

export interface UpdateTaxCategoryInput {

id: string;

name?: Maybe<string>;
}

export interface CreateShippingMethodInput {

code: string;
Expand All @@ -1193,18 +1205,6 @@ export interface UpdateShippingMethodInput {
calculator?: Maybe<ConfigurableOperationInput>;
}

export interface CreateTaxCategoryInput {

name: string;
}

export interface UpdateTaxCategoryInput {

id: string;

name?: Maybe<string>;
}

export interface CreateTaxRateInput {

name: string;
Expand Down Expand Up @@ -1676,6 +1676,7 @@ export interface ProductOptionTranslationInput {
DATETIME = "DATETIME",
BOOLEAN = "BOOLEAN",
FACET_VALUE_IDS = "FACET_VALUE_IDS",
STRING_OPERATOR = "STRING_OPERATOR",
}

export enum AdjustmentType {
Expand Down Expand Up @@ -4600,6 +4601,10 @@ export interface Query {

role?: Maybe<Role>;

taxCategories: TaxCategory[];

taxCategory?: Maybe<TaxCategory>;

shippingMethods: ShippingMethodList;

shippingMethod?: Maybe<ShippingMethod>;
Expand All @@ -4608,10 +4613,6 @@ export interface Query {

shippingCalculators: ConfigurableOperation[];

taxCategories: TaxCategory[];

taxCategory?: Maybe<TaxCategory>;

taxRates: TaxRateList;

taxRate?: Maybe<TaxRate>;
Expand Down Expand Up @@ -5753,14 +5754,14 @@ export interface Mutation {
createRole: Role;
/** Update an existing Role */
updateRole: Role;
/** Create a new ShippingMethod */
createShippingMethod: ShippingMethod;
/** Update an existing ShippingMethod */
updateShippingMethod: ShippingMethod;
/** Create a new TaxCategory */
createTaxCategory: TaxCategory;
/** Update an existing TaxCategory */
updateTaxCategory: TaxCategory;
/** Create a new ShippingMethod */
createShippingMethod: ShippingMethod;
/** Update an existing ShippingMethod */
updateShippingMethod: ShippingMethod;
/** Create a new TaxRate */
createTaxRate: TaxRate;
/** Update an existing TaxRate */
Expand Down Expand Up @@ -5961,6 +5962,10 @@ export interface RoleQueryArgs {

id: string;
}
export interface TaxCategoryQueryArgs {

id: string;
}
export interface ShippingMethodsQueryArgs {

options?: Maybe<ShippingMethodListOptions>;
Expand All @@ -5969,10 +5974,6 @@ export interface ShippingMethodQueryArgs {

id: string;
}
export interface TaxCategoryQueryArgs {

id: string;
}
export interface TaxRatesQueryArgs {

options?: Maybe<TaxRateListOptions>;
Expand Down Expand Up @@ -6205,14 +6206,6 @@ export interface UpdateRoleMutationArgs {

input: UpdateRoleInput;
}
export interface CreateShippingMethodMutationArgs {

input: CreateShippingMethodInput;
}
export interface UpdateShippingMethodMutationArgs {

input: UpdateShippingMethodInput;
}
export interface CreateTaxCategoryMutationArgs {

input: CreateTaxCategoryInput;
Expand All @@ -6221,6 +6214,14 @@ export interface UpdateTaxCategoryMutationArgs {

input: UpdateTaxCategoryInput;
}
export interface CreateShippingMethodMutationArgs {

input: CreateShippingMethodInput;
}
export interface UpdateShippingMethodMutationArgs {

input: UpdateShippingMethodInput;
}
export interface CreateTaxRateMutationArgs {

input: CreateTaxRateInput;
Expand Down
101 changes: 100 additions & 1 deletion packages/core/e2e/collection.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import {
} from '../../../admin-ui/src/app/data/definitions/collection-definitions';
import { FACET_VALUE_FRAGMENT } from '../../../admin-ui/src/app/data/definitions/facet-definitions';
import { GET_ASSET_LIST, UPDATE_PRODUCT, UPDATE_PRODUCT_VARIANTS } from '../../../admin-ui/src/app/data/definitions/product-definitions';
import { facetValueCollectionFilter } from '../src/config/collection/default-collection-filters';
import { StringOperator } from '../src/common/configurable-operation';
import { facetValueCollectionFilter, variantNameCollectionFilter } from '../src/config/collection/default-collection-filters';

import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
import { TestAdminClient } from './test-client';
Expand Down Expand Up @@ -419,6 +420,102 @@ describe('Collection resolver', () => {
});
});

describe('variantName filter', () => {

async function createVariantNameFilteredCollection(operator: StringOperator, term: string): Promise<Collection.Fragment> {
const { createCollection } = await client.query<CreateCollection.Mutation, CreateCollection.Variables>(
CREATE_COLLECTION,
{
input: {
translations: [{ languageCode: LanguageCode.en, name: `${operator} ${term}`, description: '' }],
filters: [
{
code: variantNameCollectionFilter.code,
arguments: [
{
name: 'operator',
value: operator,
type: ConfigArgType.STRING_OPERATOR,
},
{
name: 'term',
value: term,
type: ConfigArgType.STRING,
},
],
},
],
},
},
);
return createCollection;
}

it('contains operator', async () => {
const collection = await createVariantNameFilteredCollection('contains', 'camera');

const result = await client.query(GET_COLLECTION_PRODUCT_VARIANTS, {
id: collection.id,
});
expect(result.collection.productVariants.items.map((i: any) => i.name)).toEqual([
'Instant Camera',
'Camera Lens',
'SLR Camera',
]);
});

it('startsWith operator', async () => {
const collection = await createVariantNameFilteredCollection('startsWith', 'camera');

const result = await client.query(GET_COLLECTION_PRODUCT_VARIANTS, {
id: collection.id,
});
expect(result.collection.productVariants.items.map((i: any) => i.name)).toEqual([
'Camera Lens',
]);
});

it('endsWith operator', async () => {
const collection = await createVariantNameFilteredCollection('endsWith', 'camera');

const result = await client.query(GET_COLLECTION_PRODUCT_VARIANTS, {
id: collection.id,
});
expect(result.collection.productVariants.items.map((i: any) => i.name)).toEqual([
'Instant Camera',
'SLR Camera',
]);
});

it('doesNotContain operator', async () => {
const collection = await createVariantNameFilteredCollection('doesNotContain', 'camera');

const result = await client.query(GET_COLLECTION_PRODUCT_VARIANTS, {
id: collection.id,
});
expect(result.collection.productVariants.items.map((i: any) => 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',
'Gaming PC R7-2700 240GB SSD',
'Gaming PC i7-8700 120GB SSD',
'Gaming PC R7-2700 120GB SSD',
'Hard Drive 1TB',
'Hard Drive 2TB',
'Hard Drive 3TB',
'Hard Drive 4TB',
'Hard Drive 6TB',
'Clacky Keyboard',
'USB Cable',
'Tripod',
]);
});
});

describe('re-evaluation of contents on changes', () => {
let products: ProductWithVariants.Fragment[];

Expand Down Expand Up @@ -524,6 +621,8 @@ describe('Collection resolver', () => {
{ id: 'T_3', name: 'Electronics' },
{ id: 'T_5', name: 'Pear' },
{ id: 'T_7', name: 'Photo Pear' },
{ id: 'T_8', name: 'contains camera' },
{ id: 'T_10', name: 'endsWith camera' },
]);
});
});
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/api/schema/common/common-types.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ enum ConfigArgType {
DATETIME
BOOLEAN
FACET_VALUE_IDS
STRING_OPERATOR
}

type ConfigArg {
Expand Down
18 changes: 12 additions & 6 deletions packages/core/src/common/configurable-operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export type ConfigArgs<T extends ConfigArgType> = {
[name: string]: T;
};

export type StringOperator = 'startsWith' | 'endsWith' | 'contains' | 'doesNotContain';

// prettier-ignore
/**
* Represents the ConfigArgs once they have been coerced into JavaScript values for use
* in business logic.
Expand All @@ -15,12 +18,14 @@ export type ConfigArgValues<T extends ConfigArgs<any>> = {
[K in keyof T]: T[K] extends ConfigArgType.INT | ConfigArgType.MONEY | ConfigArgType.PERCENTAGE
? number
: T[K] extends ConfigArgType.DATETIME
? Date
: T[K] extends ConfigArgType.BOOLEAN
? boolean
: T[K] extends ConfigArgType.FACET_VALUE_IDS
? string[]
: string
? Date
: T[K] extends ConfigArgType.BOOLEAN
? boolean
: T[K] extends ConfigArgType.FACET_VALUE_IDS
? string[]
: T[K] extends ConfigArgType.STRING_OPERATOR
? StringOperator
: string
};

/**
Expand Down Expand Up @@ -67,6 +72,7 @@ export function argsArrayToHash<T>(args: ConfigArg[]): ConfigArgValues<T> {
function coerceValueToType<T>(arg: ConfigArg): ConfigArgValues<T>[keyof T] {
switch (arg.type as ConfigArgType) {
case ConfigArgType.STRING:
case ConfigArgType.STRING_OPERATOR:
return arg.value as any;
case ConfigArgType.INT:
case ConfigArgType.MONEY:
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/config/collection/collection-filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from '../../common/configurable-operation';
import { ProductVariant } from '../../entity/product-variant/product-variant.entity';

export type CollectionFilterArgType = ConfigArgType.FACET_VALUE_IDS;
export type CollectionFilterArgType = ConfigArgType.FACET_VALUE_IDS | ConfigArgType.STRING | ConfigArgType.STRING_OPERATOR;
export type CollectionFilterArgs = ConfigArgs<CollectionFilterArgType>;

export type ApplyCollectionFilterFn<T extends CollectionFilterArgs> = (
Expand Down Expand Up @@ -37,7 +37,7 @@ export class CollectionFilter<T extends CollectionFilterArgs = {}> implements Co
this.applyFn = config.apply;
}

apply(qb: SelectQueryBuilder<ProductVariant>, args: ConfigArg[]) {
apply(qb: SelectQueryBuilder<ProductVariant>, args: ConfigArg[]): SelectQueryBuilder<ProductVariant> {
return this.applyFn(qb, argsArrayToHash(args));
}
}
25 changes: 23 additions & 2 deletions packages/core/src/config/collection/default-collection-filters.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Brackets } from 'typeorm';

import { ConfigArgType } from '@vendure/common/lib/generated-types';
import { Brackets } from 'typeorm';

import { CollectionFilter } from './collection-filter';

Expand Down Expand Up @@ -30,3 +29,25 @@ export const facetValueCollectionFilter = new CollectionFilter({
return qb;
},
});

export const variantNameCollectionFilter = new CollectionFilter({
args: {
operator: ConfigArgType.STRING_OPERATOR,
term: ConfigArgType.STRING,
},
code: 'variant-name-filter',
description: 'Filter by ProductVariant name',
apply: (qb, args) => {
qb.leftJoin('productVariant.translations', 'translation');
switch (args.operator) {
case 'contains':
return qb.andWhere('translation.name LIKE :term', { term: `%${args.term}%` });
case 'doesNotContain':
return qb.andWhere('translation.name NOT LIKE :term', { term: `%${args.term}%` });
case 'startsWith':
return qb.andWhere('translation.name LIKE :term', { term: `${args.term}%` });
case 'endsWith':
return qb.andWhere('translation.name LIKE :term', { term: `%${args.term}` });
}
},
});
Loading

0 comments on commit 18549c7

Please sign in to comment.