feathers-factory
Version:
Create data factories for your Feathers services
327 lines (321 loc) • 10.3 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// src/index.ts
var index_exports = {};
__export(index_exports, {
Factory: () => Factory,
FactoryTemplate: () => FactoryTemplate,
GlobalFactories: () => GlobalFactories_default,
TemplateContext: () => TemplateContext
});
module.exports = __toCommonJS(index_exports);
// src/Errors/FeathersFactoryError.ts
var FeathersFactoryError = class extends Error {
};
var FeathersServiceNotDefined = class extends FeathersFactoryError {
};
// src/TemplateContext.ts
var import_clues = __toESM(require("clues"));
var TemplateContext = class _TemplateContext {
constructor(template) {
this.template = template;
this._state = Object.create(this);
const entries = Object.entries(this.template._schema).map(([key, value]) => {
return [key, this.wrapTemplateField(value)];
});
Object.assign(this._state, Object.fromEntries(entries));
}
/**
* Resolve the value of a template field within the current generator
* context. Fields are only resolved once per generator context.
*
* This ensures that you can safely reference the same field multiple times
* within the same generation context and from different fields.
*
* @example
* template = ({
* firstName: () => faker.person.firstName(),
* lastName: () => faker.person.lastName(),
*
* fullName: () => `${this.get('firstName')} ${this.get('lastName')}`,
* // -> John Doe
*
* // Functions are only called once, then cached to ensure consistent
* // results within the same generation context.
* email: () => `${this.get('firstName')}.${this.get('lastName')}@example.com`
* // -> John.Doe@example.com,
* })
*
*/
get(key) {
return (0, import_clues.default)(this._state, key, { CONTEXT: this });
}
/**
* Run the generator function for a given field. This will not cache the
* result within the current context. Meaning you can call it multiple times
* within the same generation context and it will always return a new value.
*
* This is useful if you want to extend the result of a field from within
* another field. Do keep in mind that you might want to use it sparingly
* in case the field has side-effects. E.g. creating new records in the
* database.
*
* @example
* template = ({
* firstName: () => faker.person.firstName(),
* lastName: () => faker.person.lastName(),
*
* fullName: () => `${this.get('firstName')} ${this.get('lastName')}`,
* // -> John Doe
*
* family: () => [
* this.call('fullName'), // -> <New random name>
* this.call('fullName'), // -> <New random name>
*
* this.get('fullName'), // -> John Doe
* ]
*/
call(key) {
const freshContext = new _TemplateContext(this.template);
return freshContext.get(key);
}
/**
* Wrap any template functions around an array to indicate to Clues.js
* what parameters are expected. Which is just this class instance.
*
* Enables use of the context parameter in arrow functions.
*/
wrapTemplateField(field) {
if (!this.shouldWrap(field)) {
return field;
}
return ["CONTEXT", function(CONTEXT) {
return field.apply(this, [CONTEXT]);
}];
}
/**
* Check whether the provided field is a function we should wrap to help
* Clues.js resolve input types.
*/
shouldWrap(field) {
var _a;
if (typeof field !== "function") {
return false;
}
if (((_a = Object.getOwnPropertyDescriptor(field, "prototype")) == null ? void 0 : _a.writable) === false) {
return false;
}
return true;
}
/**
* Attempt to resolve the current context state.
* Used primarily for testing. The internal state does change during
* resolve and could yield unexpected results.
* @private
*/
_resolveState() {
return __async(this, null, function* () {
const result = Object.keys(this._state).map((key) => __async(this, null, function* () {
return [key, yield this.get(key)];
}));
return Object.fromEntries(yield Promise.all(result));
});
}
};
// src/FactoryTemplate.ts
var FactoryTemplate = class _FactoryTemplate {
constructor(_schema) {
this._schema = _schema;
}
/**
* Run all factory functions in the template and return final result to be
* stored in the database.
*/
resolve(overrides) {
const template = this.extend(overrides || {});
const context = new TemplateContext(template);
return context._resolveState();
}
extend(overrides) {
return new _FactoryTemplate(__spreadValues(__spreadValues({}, this._schema), overrides));
}
};
// src/Factory.ts
var Factory = class {
/**
* Factory constructor.
*/
constructor(service, data, defaultParams = {}) {
this.service = service;
if (!service) {
throw new FeathersServiceNotDefined("The provided service doesn't appear to exist!");
}
if (data instanceof FactoryTemplate) {
this.data = data;
} else {
this.data = new FactoryTemplate(data);
}
this.params = new FactoryTemplate(defaultParams);
}
/**
* Store generated data to the Feathers service.
*/
create(data, params) {
return __async(this, null, function* () {
const resolvedData = yield this.get(data);
const resolvedParams = yield this.params.resolve(params);
return this.service.create(resolvedData, resolvedParams);
});
}
/**
* Quickly populate the database running the factory a number of times.
*/
createMany(quantity, overrides, params) {
const promises = [];
for (let i = 0; i < quantity; i++) {
promises.push(this.create(overrides, params));
}
return Promise.all(promises);
}
/**
* Just resolve a predefined factory template without inserting it into
* the underlying service.
*
* @param overrides
*/
get(overrides = {}) {
return this.data.resolve(overrides);
}
};
// src/GlobalFactories.ts
var GlobalFactories_default = new class GlobalFactories {
constructor() {
/**
* Defined factories.
*/
this.factories = {};
}
/**
* Define a new factory.
*/
define(factoryName, factory) {
this.factories[factoryName] = factory;
}
/**
* Retrieve a factory name as defined in the define() method.
*
* @param name
*/
getFactory(name) {
const factory = this.factories[name];
if (!factory) {
throw Error(`Could not locate factory '${name}'. Did you define it?`);
}
return factory;
}
/**
* Run factory, creating entry in Feathers service.
*
* @param factoryName
* @param overrides
* @param params
*/
create(factoryName, overrides, params) {
const factory = this.getFactory(factoryName);
return factory.create(overrides, params);
}
/**
* Run a number of factories, creating entries in Feathers service.
*
* @param quantity
* @param factoryName
* @param overrides
* @param params
*/
createMany(quantity, factoryName, overrides, params) {
const factory = this.getFactory(factoryName);
return factory.createMany(quantity, overrides, params);
}
/**
* Run factory without creating entry in Feathers service.
* Returns resolved data object.
*
* @param factoryName
* @param overrides
*/
get(factoryName, overrides) {
const factory = this.factories[factoryName];
if (!factory) {
throw Error(`Could not locate factory '${factoryName}'. Did you define it?`);
}
return factory.get(overrides);
}
}();
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Factory,
FactoryTemplate,
GlobalFactories,
TemplateContext
});
//# sourceMappingURL=index.js.map