@clickup/ent-framework
Version:
A PostgreSQL graph-database-alike library with microsharding and row-level security
122 lines • 4.93 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HelpersMixin = HelpersMixin;
const types_1 = require("../../types");
const EntAccessError_1 = require("../errors/EntAccessError");
const EntNotFoundError_1 = require("../errors/EntNotFoundError");
const EntUniqueKeyError_1 = require("../errors/EntUniqueKeyError");
/**
* Modifies the passed class adding convenience methods (like loadX() which
* throws when an Ent can't be loaded instead of returning null as it's done in
* the primitive operations).
*/
function HelpersMixin(Base) {
class HelpersMixin extends Base {
static async insert(vc, input) {
const id = await this.insertIfNotExists(vc, input);
if (!id) {
throw new EntUniqueKeyError_1.EntUniqueKeyError(this.name, input);
}
return id;
}
static async insertReturning(vc, input) {
const id = await this.insert(vc, input);
return this.loadX(vc, id);
}
static async upsertReturning(vc, input) {
const id = await this.upsert(vc, input);
return this.loadX(vc, id);
}
static async loadIfReadableNullable(vc, id) {
try {
return await this.loadNullable(vc, id);
}
catch (e) {
if (e instanceof EntAccessError_1.EntAccessError) {
return null;
}
throw e;
}
}
static async loadX(vc, id) {
const ent = await this.loadNullable(vc, id);
if (!ent) {
throw new EntNotFoundError_1.EntNotFoundError(this.name, { [types_1.ID]: id });
}
return ent;
}
static async loadByX(vc, input) {
const ent = await this.loadByNullable(vc, input);
if (!ent) {
throw new EntNotFoundError_1.EntNotFoundError(this.name, input);
}
return ent;
}
async updateChanged(input) {
const changedFields = [];
const changedInput = {};
// Iterate over BOTH regular fields AND symbol fields. Notice that for
// symbol fields, we'll always have a "changed" signal since the input Ent
// doesn't have them (they are to be used in triggers only).
for (const keyOrSymbol of Reflect.ownKeys(this.constructor.SCHEMA.table)) {
// ID field is always treated as immutable.
if (keyOrSymbol === types_1.ID) {
continue;
}
const field = keyOrSymbol;
const value = input[field];
const existingValue = this[field];
// Undefined is treated as "do not touch" signal for the field.
if (value === undefined) {
continue;
}
// Exact equality means "do not touch".
if (existingValue === value) {
continue;
}
// Works for most of Node built-in types: Date, Buffer, as well as for
// user-defined custom types.
if (value !== null &&
typeof value === "object" &&
existingValue !== null &&
typeof existingValue === "object" &&
JSON.stringify(value) === JSON.stringify(existingValue)) {
continue;
}
// There IS a change in this field. Record it.
changedInput[field] = value;
changedFields.push(field);
}
if (changedFields.length > 0) {
if (input.$literal) {
changedInput.$literal = input.$literal;
}
if (input.$cas) {
changedInput.$cas = input.$cas;
}
return (await this.updateOriginal(changedInput))
? changedFields
: false;
}
return null;
}
async updateChangedReturningX(input) {
return (await this.updateChanged(input))
? this.constructor.loadX(this.vc, this[types_1.ID])
: this;
}
async updateReturningNullable(input) {
const updated = await this.updateOriginal(input);
return updated ? this.constructor.loadNullable(this.vc, this[types_1.ID]) : null;
}
async updateReturningX(input) {
const res = await this.updateReturningNullable(input);
if (!res) {
throw new EntNotFoundError_1.EntNotFoundError(this.constructor.name, { [types_1.ID]: this[types_1.ID] });
}
return res;
}
}
return HelpersMixin;
}
//# sourceMappingURL=HelpersMixin.js.map