@goatlab/fluent
Version:
Readable query Interface & API generator for TS and Node
245 lines (244 loc) • 7.75 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.rejectNavigationalPropertiesInData = exports.Event = exports.Entity = exports.ValueObject = exports.Model = exports.ModelDefinition = void 0;
const relation_types_1 = require("./relation.types");
class ModelDefinition {
constructor(nameOrDef) {
if (typeof nameOrDef === 'string') {
nameOrDef = { name: nameOrDef };
}
const { name, properties, settings, relations } = nameOrDef;
this.name = name;
this.properties = {};
if (properties) {
for (const p in properties) {
this.addProperty(p, properties[p]);
}
}
this.settings = settings ?? new Map();
this.relations = relations ?? {};
}
addProperty(name, definitionOrType) {
const definition = definitionOrType.type
? definitionOrType
: { type: definitionOrType };
if (definition.id === true &&
definition.generated === true &&
definition.type !== undefined &&
definition.useDefaultIdType === undefined) {
definition.useDefaultIdType = false;
}
this.properties[name] = definition;
return this;
}
addSetting(name, value) {
this.settings[name] = value;
return this;
}
addRelation(definition) {
this.relations[definition.name] = definition;
return this;
}
belongsTo(name, definition) {
const meta = {
...definition,
name,
type: relation_types_1.RelationType.belongsTo,
targetsMany: false
};
return this.addRelation(meta);
}
hasOne(name, definition) {
const meta = {
...definition,
name,
type: relation_types_1.RelationType.hasOne,
targetsMany: false
};
return this.addRelation(meta);
}
hasMany(name, definition) {
const meta = {
...definition,
name,
type: relation_types_1.RelationType.hasMany,
targetsMany: true
};
return this.addRelation(meta);
}
idProperties() {
if (typeof this.settings.id === 'string') {
return [this.settings.id];
}
if (Array.isArray(this.settings.id)) {
return this.settings.id;
}
const idProps = Object.keys(this.properties).filter(prop => this.properties[prop].id);
return idProps;
}
}
exports.ModelDefinition = ModelDefinition;
function asJSON(value) {
if (value == null)
return value;
if (typeof value.toJSON === 'function') {
return value.toJSON();
}
if (Array.isArray(value)) {
return value.map(item => asJSON(item));
}
return value;
}
function asObject(value, options) {
if (value == null)
return value;
if (typeof value.toObject === 'function') {
return value.toObject(options);
}
if (Array.isArray(value)) {
return value.map(item => asObject(item, options));
}
return value;
}
class Model {
constructor(data) {
Object.assign(this, data);
}
static get modelName() {
return this.definition?.name || this.name;
}
toJSON() {
const def = this.constructor.definition;
if (def == null || def.settings.strict === false) {
return this.toObject({ ignoreUnknownProperties: false });
}
const copyPropertyAsJson = (key) => {
const val = asJSON(this[key]);
if (val !== undefined) {
json[key] = val;
}
};
const json = {};
const hiddenProperties = def.settings.hiddenProperties || [];
for (const p in def.properties) {
if (p in this && !hiddenProperties.includes(p)) {
copyPropertyAsJson(p);
}
}
for (const r in def.relations) {
const relName = def.relations[r].name;
if (relName in this) {
copyPropertyAsJson(relName);
}
}
return json;
}
toObject(options) {
const def = this.constructor.definition;
const obj = {};
if (options?.ignoreUnknownProperties === false) {
const hiddenProperties = def?.settings.hiddenProperties || [];
for (const p in this) {
if (!hiddenProperties.includes(p)) {
const val = this[p];
obj[p] = asObject(val, options);
}
}
return obj;
}
if (def?.relations) {
for (const r in def.relations) {
const relName = def.relations[r].name;
if (relName in this) {
obj[relName] = asObject(this[relName], {
...options,
ignoreUnknownProperties: false
});
}
}
}
const props = def.properties;
const keys = Object.keys(props);
for (const i in keys) {
const propertyName = keys[i];
const val = this[propertyName];
if (val === undefined)
continue;
obj[propertyName] = asObject(val, options);
}
return obj;
}
}
exports.Model = Model;
class ValueObject extends Model {
}
exports.ValueObject = ValueObject;
class Entity extends Model {
static getIdProperties() {
return this.definition.idProperties();
}
static getIdOf(entityOrData) {
if (typeof entityOrData.getId === 'function') {
return entityOrData.getId();
}
const idName = this.getIdProperties()[0];
return entityOrData[idName];
}
getId() {
const { definition } = this.constructor;
const idProps = definition.idProperties();
if (idProps.length === 1) {
return this[idProps[0]];
}
if (!idProps.length) {
throw new Error(`Invalid Entity ${this.constructor.name}:` +
'missing primary key (id) property');
}
return this.getIdObject();
}
getIdObject() {
const { definition } = this.constructor;
const idProps = definition.idProperties();
const idObj = {};
for (const idProp of idProps) {
idObj[idProp] = this[idProp];
}
return idObj;
}
static buildWhereForId(id) {
const where = {};
const idProps = this.definition.idProperties();
if (idProps.length === 1) {
where[idProps[0]] = id;
}
else {
for (const idProp of idProps) {
where[idProp] = id[idProp];
}
}
return where;
}
}
exports.Entity = Entity;
class Event {
}
exports.Event = Event;
function rejectNavigationalPropertiesInData(modelClass, data) {
const def = modelClass.definition;
const props = def.properties;
for (const r in def.relations) {
const relName = def.relations[r].name;
if (!(relName in data))
continue;
let msg = 'Navigational properties are not allowed in model data ' +
`(model "${modelClass.modelName}" property "${relName}"), ` +
'please remove it.';
if (relName in props) {
msg +=
' The error might be invoked by belongsTo relations, please make' +
' sure the relation name is not the same as the property name.';
}
throw new Error(msg);
}
}
exports.rejectNavigationalPropertiesInData = rejectNavigationalPropertiesInData;