UNPKG

pricing4ts

Version:

![NPM Version](https://img.shields.io/npm/v/pricing4ts) Pricing4TS is a TypeScript-based toolkit designed to enhance the server-side functionality of a pricing-driven SaaS by enabling the seamless integration of pricing plans into the application logic. T

267 lines (266 loc) 15 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.parsePricing = parsePricing; exports.formatObjectToArray = formatObjectToArray; exports.formatObject = formatObject; exports.formatArrayIntoObject = formatArrayIntoObject; var pricing_1 = require("../../models/pricing2yaml/pricing"); var pricing_validators_1 = require("../pricing-validators"); function parsePricing(extractedPricing) { var pricing = (0, pricing_1.generateEmptyPricing)(); parseBasicAttributes(extractedPricing, pricing); // Format and parse features (0, pricing_validators_1.validateFeatures)(extractedPricing.features); var formattedFeatures = formatObjectToArray(extractedPricing.features); formattedFeatures = formattedFeatures.map(function (f) { return parseFeature(f, pricing.tags); }); pricing.features = formatArrayIntoObject(formattedFeatures); // Format and parse usage limits, considering they can be null/undefined if (extractedPricing.usageLimits == null || extractedPricing.usageLimits == undefined) { pricing.usageLimits = {}; } else { (0, pricing_validators_1.validateUsageLimits)(extractedPricing.usageLimits); var formattedUsageLimits = formatObjectToArray(extractedPricing.usageLimits); formattedUsageLimits = formattedUsageLimits.map(function (u) { return parseUsageLimit(u, pricing); }); pricing.usageLimits = formatArrayIntoObject(formattedUsageLimits); } // Format and parse plans, considering they can be null/undefined if (extractedPricing.plans == null || extractedPricing.plans == undefined) { pricing.plans = {}; } else { (0, pricing_validators_1.validatePlans)(extractedPricing.plans); var plansToFormat = formatObjectToArray(extractedPricing.plans); var formattedPlans = []; for (var _i = 0, plansToFormat_1 = plansToFormat; _i < plansToFormat_1.length; _i++) { var plan = plansToFormat_1[_i]; var formattedPlan = parsePlan(plan, pricing); formattedPlans.push(formattedPlan); } pricing.plans = formatArrayIntoObject(formattedPlans); } // Format and parse add-ons, considering they can be null/undefined if (extractedPricing.addOns == null || extractedPricing.addOns == undefined) { pricing.addOns = {}; } else { var formattedAddOns = formatObjectToArray(extractedPricing.addOns); formattedAddOns = formattedAddOns.map(function (a) { return parseAddOn(a, pricing); }); pricing.addOns = formatArrayIntoObject(formattedAddOns); Object.values(pricing.addOns).forEach(function (a) { return (0, pricing_validators_1.postValidateDependsOnOrExclude)(a.dependsOn, pricing); }); Object.values(pricing.addOns).forEach(function (a) { return (0, pricing_validators_1.postValidateDependsOnOrExclude)(a.excludes, pricing); }); } if (!pricing.plans && !pricing.addOns) { throw new Error('At least one of the following must be provided: plans, addOns'); } return pricing; } // --------- PRICING ELEMENTS FORMATTERS --------- function parseBasicAttributes(extractedPricing, pricing) { pricing.syntaxVersion = (0, pricing_validators_1.validateSyntaxVersion)(extractedPricing.syntaxVersion); // Assumes that the version has been processed to be the last one pricing.saasName = (0, pricing_validators_1.validateName)(extractedPricing.saasName, 'SaaS'); pricing.url = (0, pricing_validators_1.validateUrl)(extractedPricing.url); pricing.createdAt = (0, pricing_validators_1.validateCreatedAt)(extractedPricing.createdAt); pricing.version = (0, pricing_validators_1.validateVersion)(extractedPricing.version, pricing.createdAt); // Assumes that the version has been processed to be the last one pricing.currency = (0, pricing_validators_1.validateCurrency)(extractedPricing.currency); pricing.billing = (0, pricing_validators_1.validateBilling)(extractedPricing.billing); pricing.variables = (0, pricing_validators_1.validateVariables)(extractedPricing.variables); pricing.tags = (0, pricing_validators_1.validateTags)(extractedPricing.tags); pricing.custom = (0, pricing_validators_1.validateCustom)(extractedPricing.custom); } function parseFeature(feature, tags) { var featureName = feature.name; try { (0, pricing_validators_1.validateFeature)(feature); feature.name = (0, pricing_validators_1.validateName)(feature.name, 'Feature'); feature.description = (0, pricing_validators_1.validateDescription)(feature.description); feature.valueType = (0, pricing_validators_1.validateValueType)(feature.valueType); feature.defaultValue = (0, pricing_validators_1.validateDefaultValue)(feature, 'feature'); feature.value = (0, pricing_validators_1.validateValue)(feature, 'feature'); feature.expression = (0, pricing_validators_1.validateExpression)(feature.expression, 'expression'); feature.serverExpression = (0, pricing_validators_1.validateExpression)(feature.serverExpression, 'serverExpression'); feature.type = (0, pricing_validators_1.validateFeatureType)(feature.type); feature.integrationType = (0, pricing_validators_1.validateFeatureIntegrationType)(feature.integrationType, feature.type); feature.pricingUrls = (0, pricing_validators_1.validatePricingUrls)(feature.type, feature.integrationType, feature.pricingUrls); feature.docUrl = (0, pricing_validators_1.validateDocUrl)(feature.type, feature.docUrl); feature.automationType = (0, pricing_validators_1.validateFeatureAutomationType)(feature.automationType, feature.type); feature.render = (0, pricing_validators_1.validateRenderMode)(feature.render); feature.tag = validateTag(feature.tag, tags); } catch (err) { throw new Error("Error parsing feature ".concat(featureName, ". Error: ").concat(err.message)); } return feature; } function parseUsageLimit(usageLimit, pricing) { var _a, _b; try { (0, pricing_validators_1.validateUsageLimit)(usageLimit); usageLimit.name = (0, pricing_validators_1.validateName)(usageLimit.name, 'Usage Limit'); usageLimit.description = (0, pricing_validators_1.validateDescription)(usageLimit.description); usageLimit.valueType = (0, pricing_validators_1.validateValueType)(usageLimit.valueType); usageLimit.defaultValue = (0, pricing_validators_1.validateDefaultValue)(usageLimit, 'usage limit'); usageLimit.value = (0, pricing_validators_1.validateValue)(usageLimit, 'usage limit'); usageLimit.unit = (0, pricing_validators_1.validateUnit)(usageLimit.unit); usageLimit.type = (0, pricing_validators_1.validateUsageLimitType)(usageLimit.type); if (usageLimit.type === 'RENEWABLE') { usageLimit.period = { unit: (0, pricing_validators_1.validatePeriodUnit)((_a = usageLimit.period) === null || _a === void 0 ? void 0 : _a.unit), value: (0, pricing_validators_1.validatePeriodValue)((_b = usageLimit.period) === null || _b === void 0 ? void 0 : _b.value), }; } else if (usageLimit.type === 'NON_RENEWABLE') { usageLimit.trackable = (0, pricing_validators_1.validateTrackable)(usageLimit.trackable); } if (usageLimit.trackable === undefined && !usageLimit.period) { throw new Error("Usage limit ".concat(usageLimit.name, " must have a trackable property or a period defined")); } usageLimit.linkedFeatures = (0, pricing_validators_1.validateLinkedFeatures)(usageLimit.linkedFeatures, pricing); usageLimit.render = (0, pricing_validators_1.validateRenderMode)(usageLimit.render); } catch (err) { throw new Error("Error parsing usage limit ".concat(usageLimit.name, ". Error: ").concat(err.message)); } return usageLimit; } function parsePlan(plan, pricing) { var _a; try { (0, pricing_validators_1.validatePlan)(plan); plan.name = (0, pricing_validators_1.validateName)(plan.name, 'Plan'); plan.description = (0, pricing_validators_1.validateDescription)(plan.description); plan.price = (0, pricing_validators_1.validatePrice)(plan.price, pricing.variables); plan.unit = (0, pricing_validators_1.validateUnit)(plan.unit); plan.private = (0, pricing_validators_1.validatePrivate)(plan.private); var planFeatures = JSON.parse(JSON.stringify(pricing.features)); // This is performed in order to avoid modifying the original object if (plan.features !== null && plan.features !== undefined) { plan.features = formatObject((_a = plan.features) !== null && _a !== void 0 ? _a : {}); } else { plan.features = {}; } plan.features = (0, pricing_validators_1.validatePlanFeatures)(plan, planFeatures); var planUsageLimits = JSON.parse(JSON.stringify(pricing.usageLimits)); if (plan.usageLimits !== null && plan.usageLimits !== undefined) { plan.usageLimits = formatObject(plan.usageLimits); } else { plan.usageLimits = {}; } plan.usageLimits = (0, pricing_validators_1.validatePlanUsageLimits)(plan, planUsageLimits); } catch (err) { throw new Error("Error parsing plan ".concat(plan.name, ". Error: ").concat(err.message)); } return plan; } function parseAddOn(addon, pricing) { var _a, _b, _c; try { addon.name = (0, pricing_validators_1.validateName)(addon.name, 'Addon'); addon.description = (0, pricing_validators_1.validateDescription)(addon.description); addon.availableFor = (0, pricing_validators_1.validateAvailableFor)(addon.availableFor, pricing); addon.dependsOn = (0, pricing_validators_1.validateDependsOnOrExcludes)(addon.dependsOn, pricing, "dependsOn"); addon.excludes = (0, pricing_validators_1.validateDependsOnOrExcludes)(addon.excludes, pricing, "excludes"); addon.price = (0, pricing_validators_1.validatePrice)(addon.price, pricing.variables); addon.unit = (0, pricing_validators_1.validateUnit)(addon.unit); addon.private = (0, pricing_validators_1.validatePrivate)(addon.private); // Parse Features if provided if (addon.features !== null && addon.features !== undefined) { var addonFeatures = JSON.parse(JSON.stringify(pricing.features)); addon.features = formatObject(addon.features); addon.features = (0, pricing_validators_1.validateAddonFeatures)(addon, addonFeatures); } else { addon.features = {}; } // Parse UsageLimits if provided if (addon.usageLimits !== null && addon.usageLimits !== undefined) { var addonUsageLimits = JSON.parse(JSON.stringify(pricing.usageLimits)); addon.usageLimits = formatObject(addon.usageLimits); addon.usageLimits = (0, pricing_validators_1.validateAddonUsageLimits)(addon, addonUsageLimits); } else { addon.usageLimits = {}; } // Parse usageLimitsExtensions if provided if (addon.usageLimitsExtensions !== null && addon.usageLimitsExtensions !== undefined) { var addonUsageLimitsExtensions = JSON.parse(JSON.stringify(pricing.usageLimits)); addon.usageLimitsExtensions = formatObject(addon.usageLimitsExtensions); addon.usageLimitsExtensions = (0, pricing_validators_1.validateAddonUsageLimitsExtensions)(addon, addonUsageLimitsExtensions); } else { addon.usageLimitsExtensions = {}; } if (Object.keys(addon.features).length === 0 && Object.keys(addon.usageLimits).length === 0 && Object.keys(addon.usageLimitsExtensions).length === 0) { throw new Error('An add-on cannot be empty. It must have at least one feature, usage limit or usage limit extension'); } var minQuantity = (0, pricing_validators_1.validateSubscriptionConstraintMinQuantity)((_a = addon.subscriptionConstraints) === null || _a === void 0 ? void 0 : _a.minQuantity); var isScalableAddon = Object.keys(addon.features).length === 0 && Object.keys(addon.usageLimits).length === 0 && Object.keys(addon.usageLimitsExtensions).length > 0; if (isScalableAddon) { addon.subscriptionConstraints = { minQuantity: minQuantity, maxQuantity: (0, pricing_validators_1.validateSubscriptionConstraintMaxQuantity)((_b = addon.subscriptionConstraints) === null || _b === void 0 ? void 0 : _b.maxQuantity, minQuantity), quantityStep: (0, pricing_validators_1.validateSubscriptionConstraintQuantityStep)((_c = addon.subscriptionConstraints) === null || _c === void 0 ? void 0 : _c.quantityStep, minQuantity), }; } } catch (err) { throw new Error("Error parsing addon ".concat(addon.name, ". Error: ").concat(err.message)); } return addon; } function validateTag(tag, tags) { try { if (tag && tags && !tags.includes(tag)) { throw new Error("Feature tag '".concat(tag, "' must be one of the values defined in 'tags'")); } return tag; } catch (err) { throw new Error("Error validating tag: ".concat(err.message)); } } // --------- UTILITY FUNCTIONS --------- function formatObjectToArray(object) { return Object.entries(object).map(function (_a) { var name = _a[0], details = _a[1]; return (__assign({ name: name }, details)); }); } function formatObject(object) { return Object.entries(object).reduce(function (acc, _a) { var key = _a[0], value = _a[1]; acc[key] = __assign({ name: key }, value); return acc; }, {}); } function formatArrayIntoObject(array) { return array.reduce(function (acc, _a) { var name = _a.name, rest = __rest(_a, ["name"]); acc[name] = __assign({ name: name }, rest); return acc; }, {}); }