@decaf-ts/for-couchdb
Version:
decaf-ts couchdb wrappers
155 lines • 7.03 kB
JavaScript
import { Repository } from "@decaf-ts/core";
import { Model } from "@decaf-ts/decorator-validation";
import { getMetadata, setMetadata } from "./metadata.js";
import { enforceDBDecorators, reduceErrorsToPrint, InternalError, OperationKeys, ValidationError, BulkCrudOperationKeys, } from "@decaf-ts/db-decorators";
export class CouchDBRepository extends Repository {
constructor(adapter, model, force = false) {
super(adapter, model, force);
}
assignMetadata(target, source) {
const apply = (instance, carrier) => {
const metadataSource = carrier ?? instance;
const metadata = getMetadata(metadataSource);
if (metadata)
setMetadata(instance, metadata);
return instance;
};
if (Array.isArray(target)) {
return target.map((model, index) => {
const carrier = Array.isArray(source) ? source[index] : source;
return apply(model, carrier);
});
}
const carrier = Array.isArray(source) ? source?.[0] : source;
return apply(target, carrier);
}
async create(model, ...args) {
const result = await super.create(model, ...args);
this.assignMetadata(result);
this.assignMetadata(model, result);
return result;
}
async createAll(models, ...args) {
const results = await super.createAll(models, ...args);
this.assignMetadata(results);
this.assignMetadata(models, results);
return results;
}
async read(id, ...args) {
const result = await super.read(id, ...args);
return this.assignMetadata(result);
}
async readAll(ids, ...args) {
const results = await super.readAll(ids, ...args);
return this.assignMetadata(results);
}
async update(model, ...args) {
const result = await super.update(model, ...args);
this.assignMetadata(result);
this.assignMetadata(model, result);
return result;
}
async updateAll(models, ...args) {
const results = await super.updateAll(models, ...args);
this.assignMetadata(results);
this.assignMetadata(models, results);
return results;
}
async delete(id, ...args) {
const result = await super.delete(id, ...args);
return this.assignMetadata(result);
}
async deleteAll(ids, ...args) {
const results = await super.deleteAll(ids, ...args);
return this.assignMetadata(results);
}
/**
* @description Prepares a model for update.
* @summary Validates the model and prepares it for update in the database.
* @param {M} model - The model to update.
* @param {...any[]} args - Additional arguments.
* @return The prepared model and context arguments.
* @throws {InternalError} If the model has no primary key value.
* @throws {ValidationError} If the model fails validation.
*/
async updatePrefix(model, ...args) {
const { ctx, ctxArgs, log } = (await this.logCtx(args, OperationKeys.UPDATE, true)).for(this.updatePrefix);
const ignoreHandlers = ctx.get("ignoreHandlers");
const ignoreValidate = ctx.get("ignoreValidation");
log.silly(`handlerSetting: ${ignoreHandlers}, validationSetting: ${ignoreValidate}`);
const pk = model[this.pk];
if (!pk)
throw new InternalError(`No value for the Id is defined under the property ${this.pk}`);
let oldModel;
let oldMetadata;
if (ctx.get("applyUpdateValidation")) {
oldModel = await this.read(pk);
oldMetadata = oldModel ? getMetadata(oldModel) : undefined;
if (ctx.get("mergeForUpdate"))
model = Model.merge(oldModel, model, this.class);
}
if (!ignoreHandlers)
await enforceDBDecorators(this, ctx, model, OperationKeys.UPDATE, OperationKeys.ON, oldModel);
if (!ignoreValidate) {
const propsToIgnore = ctx.get("ignoredValidationProperties") || [];
log.silly(`ignored validation properties: ${propsToIgnore}`);
const errors = await Promise.resolve(model.hasErrors(oldModel, ...propsToIgnore));
if (errors)
throw new ValidationError(errors.toString());
}
if (oldMetadata)
setMetadata(model, oldMetadata);
return [model, ...ctxArgs, oldModel];
}
/**
* @description Prepares multiple models for update.
* @summary Validates multiple models and prepares them for update in the database.
* @param {M[]} models - The models to update.
* @param {...any[]} args - Additional arguments.
* @return {Promise<any[]>} The prepared models and context arguments.
* @throws {InternalError} If any model has no primary key value.
* @throws {ValidationError} If any model fails validation.
*/
async updateAllPrefix(models, ...args) {
const { ctx, ctxArgs, log } = (await this.logCtx(args, BulkCrudOperationKeys.UPDATE_ALL, true)).for(this.updateAllPrefix);
const ignoreHandlers = ctx.get("ignoreHandlers");
const ignoreValidate = ctx.get("ignoreValidation");
log.silly(`handlerSetting: ${ignoreHandlers}, ignoredValidation: ${ignoreValidate}`);
const ids = models.map((m) => {
const id = m[this.pk];
if (!id)
throw new InternalError("missing id on update operation");
return id;
});
let oldModels;
if (ctx.get("applyUpdateValidation")) {
oldModels = await this.readAll(ids, ctx);
models = models.map((m, i) => {
if (ctx.get("mergeForUpdate"))
m = Model.merge(oldModels[i], m, this.class);
const oldMetadata = getMetadata(oldModels[i]);
if (oldMetadata)
setMetadata(m, oldMetadata);
return m;
});
}
if (!ignoreHandlers)
await Promise.all(models.map((m, i) => enforceDBDecorators(this, ctx, m, OperationKeys.UPDATE, OperationKeys.ON, oldModels ? oldModels[i] : undefined)));
if (!ignoreValidate) {
const ignoredProps = ctx.get("ignoredValidationProperties") || [];
log.silly(`ignored validation properties: ${ignoredProps}`);
let modelsValidation;
if (!ctx.get("applyUpdateValidation")) {
modelsValidation = await Promise.resolve(models.map((m) => m.hasErrors(...ignoredProps)));
}
else {
modelsValidation = await Promise.all(models.map((m, i) => Promise.resolve(m.hasErrors(oldModels[i], ...ignoredProps))));
}
const errorMessages = reduceErrorsToPrint(modelsValidation);
if (errorMessages)
throw new ValidationError(errorMessages);
}
return [models, ...ctxArgs, oldModels];
}
}
//# sourceMappingURL=repository.js.map