UNPKG

@vendure/core

Version:

A modern, headless ecommerce framework

192 lines 6.94 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfigurableOperationDef = void 0; const shared_utils_1 = require("@vendure/common/lib/shared-utils"); const constants_1 = require("./constants"); const errors_1 = require("./error/errors"); /** * @description * A ConfigurableOperationDef is a special type of object used extensively by Vendure to define * code blocks which have arguments which are configurable at run-time by the administrator. * * This is the mechanism used by: * * * {@link CollectionFilter} * * {@link PaymentMethodHandler} * * {@link PromotionAction} * * {@link PromotionCondition} * * {@link ShippingCalculator} * * {@link ShippingEligibilityChecker} * * Any class which extends ConfigurableOperationDef works in the same way: it takes a * config object as the constructor argument. That config object extends the {@link ConfigurableOperationDefOptions} * interface and typically adds some kind of business logic function to it. * * For example, in the case of `ShippingEligibilityChecker`, * it adds the `check()` function to the config object which defines the logic for checking whether an Order is eligible * for a particular ShippingMethod. * * ## The `args` property * * The key feature of the ConfigurableOperationDef is the `args` property. This is where we define those * arguments that are exposed via the Admin UI as data input components. This allows their values to * be set at run-time by the Administrator. Those values can then be accessed in the business logic * of the operation. * * The data type of the args can be one of {@link ConfigArgType}, and the configuration is further explained in * the docs of {@link ConfigArgs}. * * ## Dependency Injection * If your business logic relies on injectable providers, such as the `TransactionalConnection` object, or any of the * internal Vendure services or those defined in a plugin, you can inject them by using the config object's * `init()` method, which exposes the {@link Injector}. * * Here's an example of a ShippingCalculator that injects a service which has been defined in a plugin: * * @example * ```ts * import { Injector, ShippingCalculator } from '\@vendure/core'; * import { ShippingRatesService } from './shipping-rates.service'; * * // We keep reference to our injected service by keeping it * // in the top-level scope of the file. * let shippingRatesService: ShippingRatesService; * * export const customShippingCalculator = new ShippingCalculator({ * code: 'custom-shipping-calculator', * description: [], * args: {}, * * init(injector: Injector) { * // The init function is called during bootstrap, and allows * // us to inject any providers we need. * shippingRatesService = injector.get(ShippingRatesService); * }, * * calculate: async (order, args) => { * // We can now use the injected provider in the business logic. * const { price, priceWithTax } = await shippingRatesService.getRate({ * destination: order.shippingAddress, * contents: order.lines, * }); * * return { * price, * priceWithTax, * }; * }, * }); * ``` * * @docsCategory ConfigurableOperationDef */ class ConfigurableOperationDef { get code() { return this.options.code; } get args() { return this.options.args; } get description() { return this.options.description; } constructor(options) { this.options = options; } async init(injector) { if (typeof this.options.init === 'function') { await this.options.init(injector); } } async destroy() { if (typeof this.options.destroy === 'function') { await this.options.destroy(); } } /** * @description * Convert a ConfigurableOperationDef into a ConfigurableOperationDefinition object, typically * so that it can be sent via the API. */ toGraphQlType(ctx) { return { code: this.code, description: localizeString(this.description, ctx.languageCode, ctx.channel.defaultLanguageCode), args: Object.entries(this.args).map(([name, arg]) => { var _a, _b; return ({ name, type: arg.type, list: (_a = arg.list) !== null && _a !== void 0 ? _a : false, required: (_b = arg.required) !== null && _b !== void 0 ? _b : true, defaultValue: arg.defaultValue, ui: arg.ui, label: arg.label && localizeString(arg.label, ctx.languageCode, ctx.channel.defaultLanguageCode), description: arg.description && localizeString(arg.description, ctx.languageCode, ctx.channel.defaultLanguageCode), }); }), }; } /** * @description * Coverts an array of ConfigArgs into a hash object: * * from: * `[{ name: 'foo', type: 'string', value: 'bar'}]` * * to: * `{ foo: 'bar' }` **/ argsArrayToHash(args) { const output = {}; for (const arg of args) { if (arg && arg.value != null && this.args[arg.name] != null) { output[arg.name] = coerceValueToType(arg.value, this.args[arg.name].type, this.args[arg.name].list || false); } } return output; } } exports.ConfigurableOperationDef = ConfigurableOperationDef; function localizeString(stringArray, languageCode, channelLanguageCode) { let match = stringArray.find(x => x.languageCode === languageCode); if (!match) { match = stringArray.find(x => x.languageCode === channelLanguageCode); } if (!match) { match = stringArray.find(x => x.languageCode === constants_1.DEFAULT_LANGUAGE_CODE); } if (!match) { match = stringArray[0]; } return match.value; } function coerceValueToType(value, type, isList) { if (isList) { try { return JSON.parse(value).map(v => coerceValueToType(v, type, false)); } catch (err) { throw new errors_1.InternalServerError(`Could not parse list value "${value}": ` + JSON.stringify(err.message)); } } switch (type) { case 'string': return value; case 'int': return Number.parseInt(value || '', 10); case 'float': return Number.parseFloat(value || ''); case 'datetime': return Date.parse(value || ''); case 'boolean': return !!(value && (value.toLowerCase() === 'true' || value === '1')); case 'ID': return value; default: (0, shared_utils_1.assertNever)(type); } } //# sourceMappingURL=configurable-operation.js.map