Skip to content

Commit

Permalink
feat(core): Add support for list types in ConfigurableOperationDefs
Browse files Browse the repository at this point in the history
Relates to #414

BREAKING CHANGE: The `'facetValueIds'` type has been removed from the `ConfigArgType` type, and replaced by `'ID'` and the `list` option. This change only affects you if you have created custom CollectionFilters of PromotionActions/Conditions using the `'facetValueIds'` type for an argument.
  • Loading branch information
michaelbromley committed Jul 29, 2020
1 parent 1a37148 commit 6698195
Show file tree
Hide file tree
Showing 11 changed files with 52 additions and 36 deletions.
2 changes: 1 addition & 1 deletion packages/common/src/shared-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export type CustomFieldType = 'string' | 'localeString' | 'int' | 'float' | 'boo
* @docsCategory common
* @docsPage Configurable Operations
*/
export type ConfigArgType = 'string' | 'int' | 'float' | 'boolean' | 'datetime' | 'facetValueIds';
export type ConfigArgType = 'string' | 'int' | 'float' | 'boolean' | 'datetime' | 'ID';

export type ConfigArgSubset<T extends ConfigArgType> = T;

Expand Down
2 changes: 1 addition & 1 deletion packages/core/e2e/promotion.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ function generateTestAction(code: string): PromotionAction<any> {
return new PromotionOrderAction({
code,
description: [{ languageCode: LanguageCode.en, value: `description for ${code}` }],
args: { facetValueIds: { type: 'facetValueIds' } },
args: { facetValueIds: { type: 'ID', list: true } },
execute: (order, args) => {
return 42;
},
Expand Down
30 changes: 19 additions & 11 deletions packages/core/src/api/common/configurable-operation-codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,15 @@ export class ConfigurableOperationCodec {
}
for (const arg of operationInput.arguments) {
const argDef = def.args[arg.name];
if (argDef.type === 'facetValueIds' && arg.value) {
const ids = JSON.parse(arg.value) as string[];
const decodedIds = ids.map(id => this.idCodecService.decode(id));
arg.value = JSON.stringify(decodedIds);
if (argDef.type === 'ID' && arg.value) {
if (argDef.list === true) {
const ids = JSON.parse(arg.value) as string[];
const decodedIds = ids.map(id => this.idCodecService.decode(id));
arg.value = JSON.stringify(decodedIds);
} else {
const decodedId = this.idCodecService.decode(arg.value);
arg.value = JSON.stringify(decodedId);
}
}
}
}
Expand All @@ -61,19 +66,22 @@ export class ConfigurableOperationCodec {
}
for (const arg of operationInput.args) {
const argDef = def.args[arg.name];
if (argDef.type === 'facetValueIds' && arg.value) {
const ids = JSON.parse(arg.value) as string[];
const encodedIds = ids.map(id => this.idCodecService.encode(id));
arg.value = JSON.stringify(encodedIds);
if (argDef.type === 'ID' && arg.value) {
if (argDef.list === true) {
const ids = JSON.parse(arg.value) as string[];
const encodedIds = ids.map(id => this.idCodecService.encode(id));
arg.value = JSON.stringify(encodedIds);
} else {
const encodedId = this.idCodecService.encode(arg.value);
arg.value = JSON.stringify(encodedId);
}
}
}
}
return input;
}

getAvailableDefsOfType(
defType: Type<ConfigurableOperationDef<any>>,
): Array<ConfigurableOperationDef<any>> {
getAvailableDefsOfType(defType: Type<ConfigurableOperationDef>): ConfigurableOperationDef[] {
switch (defType) {
case CollectionFilter:
return this.configService.catalogOptions.collectionFilters;
Expand Down
38 changes: 24 additions & 14 deletions packages/core/src/common/configurable-operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
Maybe,
StringFieldOption,
} from '@vendure/common/lib/generated-types';
import { ConfigArgType } from '@vendure/common/lib/shared-types';
import { ConfigArgType, ID } from '@vendure/common/lib/shared-types';
import { assertNever } from '@vendure/common/lib/shared-utils';
import { simpleDeepClone } from '@vendure/common/lib/simple-deep-clone';

import { RequestContext } from '../api/common/request-context';
Expand Down Expand Up @@ -112,8 +113,10 @@ export type ConfigArgValues<T extends ConfigArgs<any>> = {
? boolean[]
: T[K] extends ConfigArgDef<'boolean'>
? boolean
: T[K] extends ConfigArgDef<'facetValueIds'>
? string[]
: T[K] extends ConfigArgListDef<'ID'>
? ID[]
: T[K] extends ConfigArgDef<'ID'>
? ID
: T[K] extends ConfigArgListDef<'string'>
? string[]
: string;
Expand Down Expand Up @@ -165,7 +168,7 @@ export interface ConfigurableOperationDefOptions<T extends ConfigArgs<ConfigArgT
* @docsCategory common
* @docsPage Configurable Operations
*/
export class ConfigurableOperationDef<T extends ConfigArgs<ConfigArgType>> {
export class ConfigurableOperationDef<T extends ConfigArgs<ConfigArgType> = ConfigArgs<ConfigArgType>> {
get code(): string {
return this.options.code;
}
Expand Down Expand Up @@ -225,7 +228,8 @@ export class ConfigurableOperationDef<T extends ConfigArgs<ConfigArgType>> {
if (arg && arg.value != null) {
output[arg.name as keyof ConfigArgValues<T>] = coerceValueToType<T>(
arg.value,
this.args[arg.name],
this.args[arg.name].type,
this.args[arg.name].list || false,
);
}
}
Expand Down Expand Up @@ -266,24 +270,30 @@ function localizeString(stringArray: LocalizedStringArray, languageCode: Languag

function coerceValueToType<T extends ConfigArgs<any>>(
value: string,
argDef: ConfigArgDef<any>,
type: ConfigArgType,
isList: boolean,
): ConfigArgValues<T>[keyof T] {
switch (argDef.type as ConfigArgType) {
if (isList) {
try {
return (JSON.parse(value) as string[]).map(v => coerceValueToType(v, type, false)) as any;
} catch (err) {
throw new InternalServerError(err.message);
}
}
switch (type) {
case 'string':
return value as any;
case 'int':
return Number.parseInt(value || '', 10) as any;
case 'float':
return Number.parseFloat(value || '') as any;
case 'datetime':
return Date.parse(value || '') as any;
case 'boolean':
return !!(value && (value.toLowerCase() === 'true' || value === '1')) as any;
case 'facetValueIds':
try {
return JSON.parse(value as any);
} catch (err) {
throw new InternalServerError(err.message);
}
case 'ID':
return value as any;
default:
return (value as string) as any;
assertNever(type);
}
}
2 changes: 1 addition & 1 deletion packages/core/src/config/collection/collection-filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from '../../common/configurable-operation';
import { ProductVariant } from '../../entity/product-variant/product-variant.entity';

export type CollectionFilterArgType = ConfigArgSubset<'facetValueIds' | 'string' | 'boolean'>;
export type CollectionFilterArgType = ConfigArgSubset<'ID' | 'string' | 'boolean'>;
export type CollectionFilterArgs = ConfigArgs<CollectionFilterArgType>;

export type ApplyCollectionFilterFn<T extends CollectionFilterArgs> = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { CollectionFilter } from './collection-filter';
*/
export const facetValueCollectionFilter = new CollectionFilter({
args: {
facetValueIds: { type: 'facetValueIds' },
facetValueIds: { type: 'ID', list: true },
containsAny: { type: 'boolean' },
},
code: 'facet-value-filter',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export const discountOnItemWithFacets = new PromotionItemAction({
},
},
facets: {
type: 'facetValueIds',
type: 'ID',
list: true,
},
},
async execute(orderItem, orderLine, args, { hasFacetValues }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const atLeastNWithFacets = new PromotionCondition({
],
args: {
minimum: { type: 'int' },
facets: { type: 'facetValueIds' },
facets: { type: 'ID', list: true },
},
async check(order: Order, args, { hasFacetValues }) {
let matches = 0;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/config/promotion/promotion-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Order } from '../../entity/order/order.entity';

import { PromotionUtils } from './promotion-condition';

export type PromotionActionArgType = ConfigArgSubset<'int' | 'facetValueIds'>;
export type PromotionActionArgType = ConfigArgSubset<'int' | 'ID'>;
export type PromotionActionArgs = ConfigArgs<PromotionActionArgType>;

/**
Expand Down
4 changes: 1 addition & 3 deletions packages/core/src/config/promotion/promotion-condition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import {
import { OrderLine } from '../../entity';
import { Order } from '../../entity/order/order.entity';

export type PromotionConditionArgType = ConfigArgSubset<
'int' | 'string' | 'datetime' | 'boolean' | 'facetValueIds'
>;
export type PromotionConditionArgType = ConfigArgSubset<'int' | 'string' | 'datetime' | 'boolean' | 'ID'>;
export type PromotionConditionArgs = ConfigArgs<PromotionConditionArgType>;

/**
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/service/services/collection.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,6 @@ export class CollectionService implements OnModuleInit {
args: filter.arguments.map((inputArg, i) => {
return {
name: inputArg.name,
type: match.args[inputArg.name].type,
value: inputArg.value,
};
}),
Expand Down

0 comments on commit 6698195

Please sign in to comment.