@decaf-ts/core
Version:
Core persistence module for the decaf framework
391 lines • 15.7 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ModelService = exports.ClientBasedService = exports.Service = void 0;
const db_decorators_1 = require("@decaf-ts/db-decorators");
const logging_1 = require("@decaf-ts/logging");
const injectable_decorators_1 = require("@decaf-ts/injectable-decorators");
const Repository_1 = require("./../repository/Repository.cjs");
const decorators_1 = require("./decorators.cjs");
const Context_1 = require("./../persistence/Context.cjs");
const constants_1 = require("./../persistence/constants.cjs");
class Service {
constructor(name) {
this.name = name;
/**
* @description The context constructor for this adapter
* @summary Reference to the context class constructor used by this adapter
*/
this.Context = (Context_1.Context);
}
/**
* @description Creates repository flags for an operation
* @summary Generates a set of flags that describe a database operation, combining default flags with overrides
* @template F - The Repository Flags type
* @template M - The model type
* @param {OperationKeys} operation - The type of operation being performed
* @param {Constructor<M>} model - The model constructor
* @param {Partial<F>} flags - Custom flag overrides
* @param {...any[]} args - Additional arguments
* @return {Promise<F>} The complete set of flags
*/
async flags(operation, flags,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
...args) {
let log = (flags.logger || logging_1.Logging.for(this.toString()));
if (flags.correlationId)
log = log.for({ correlationId: flags.correlationId });
return Object.assign({}, constants_1.DefaultAdapterFlags, flags, {
timestamp: new Date(),
operation: operation,
logger: log,
});
}
async context(operation, overrides, ...args) {
const normalizedOverrides = overrides;
const flags = await this.flags(operation, normalizedOverrides, ...args);
return new this.Context().accumulate(flags);
}
async logCtx(args, method, allowCreate = false) {
return (await Service.logCtx.bind(this)(args, method, allowCreate));
}
static async logCtx(args, operation, allowCreate = false,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
...argz) {
const bootCtx = async function bootCtx() {
if (!allowCreate)
throw new db_decorators_1.InternalError("No context provided");
return this.context(typeof operation === "string" ? operation : operation.name, {});
}.bind(this);
if (args.length < 1) {
args = [await bootCtx()];
}
const ctx = args.pop();
if (!(ctx instanceof Context_1.Context))
args = [...args, await bootCtx()];
const log = (this
? ctx.logger.for(this).for(operation)
: ctx.logger.clear().for(this).for(operation));
return {
ctx: ctx,
log: operation ? log.for(operation) : log,
ctxArgs: [...args, ctx],
};
}
/**
* @description Retrieves a Service instance by name/class
* @summary Looks up and returns a cached API instance by its name or constructor
* @template A Type extending Api
* @param {string | Constructor<A>} name - Name of the API or its constructor
* @return {A} The requested API instance
*/
static get(name) {
if (!name)
throw new db_decorators_1.InternalError(`No name provided`);
const injectable = injectable_decorators_1.Injectables.get(name);
if (injectable)
return injectable;
throw new db_decorators_1.InternalError(`No Service found for ${typeof name === "string" ? name : typeof name === "symbol" ? name.toString() : name.name}`);
}
static async boot(...args) {
const factory = {
async context(operation) {
return new Context_1.Context().accumulate(Object.assign({}, constants_1.DefaultAdapterFlags, {
timestamp: new Date(),
operation: operation,
logger: logging_1.Logging.get(),
}));
},
};
const { log, ctxArgs } = await this.logCtx.bind(factory)(args, this.boot, true);
const services = injectable_decorators_1.Injectables.services();
for (const [key, service] of Object.entries(services)) {
try {
const s = new service();
if (s instanceof ClientBasedService)
await s.boot(...ctxArgs);
}
catch (e) {
log.error(`Failed to boot ${key} service`, e);
}
}
}
}
exports.Service = Service;
class ClientBasedService extends Service {
constructor() {
super();
}
async boot(...args) {
const { log, ctxArgs } = await this.logCtx(args, this.boot, true);
log.verbose(`Initializing ${this.toString()}...`);
const { config, client } = await this.initialize(...ctxArgs);
this._config = config;
this._client = client;
}
get config() {
if (!this._config)
throw new db_decorators_1.InternalError(`Config not initialized`);
return this._config;
}
get client() {
if (!this._client)
throw new db_decorators_1.InternalError(`Client not initialized`);
return this._client;
}
async shutdown(...args) {
const { log } = await this.logCtx(args, this.shutdown, true);
log.info(`Shutting down ${this.name} service...`);
}
}
exports.ClientBasedService = ClientBasedService;
__decorate([
(0, logging_1.final)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], ClientBasedService.prototype, "boot", null);
__decorate([
(0, logging_1.final)(),
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], ClientBasedService.prototype, "config", null);
__decorate([
(0, logging_1.final)(),
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], ClientBasedService.prototype, "client", null);
const resolveAlias = (alias) => {
if (typeof alias === "string")
return alias.endsWith("Service") ? alias : `${alias}Service`;
if (typeof alias === "symbol")
return alias.toString();
return `${alias.name}Service`;
};
class ModelService extends Service {
get class() {
if (!this.clazz)
throw new db_decorators_1.InternalError(`Class not initialized`);
return this.clazz;
}
get repo() {
if (!this._repository)
this._repository = Repository_1.Repository.forModel(this.clazz);
return this._repository;
}
constructor(clazz, name) {
super(name ?? `${clazz.name}Service`);
this.clazz = clazz;
}
static getService(name) {
if (!name)
throw new db_decorators_1.InternalError(`No name provided`);
const alias = resolveAlias(name);
try {
const injectable = Service.get(alias);
if (injectable)
return injectable;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}
catch (e) {
// ignore
}
throw new db_decorators_1.InternalError(`No ModelService found for alias ${alias}`);
}
for(conf, ...args) {
const target = this;
return new Proxy(target, {
get(original, prop, receiver) {
if (prop === "repo") {
return original.repo.for(conf, ...args);
}
return Reflect.get(original, prop, receiver);
},
});
}
async create(model, ...args) {
const { ctxArgs } = await this.logCtx(args, this.create, true);
return this.repo.create(model, ...ctxArgs);
}
async createAll(models, ...args) {
const { ctxArgs } = await this.logCtx(args, this.createAll, true);
return this.repo.createAll(models, ...ctxArgs);
}
async delete(key, ...args) {
const { ctxArgs } = await this.logCtx(args, this.delete, true);
return this.repo.delete(key, ...ctxArgs);
}
async deleteAll(keys, ...args) {
const { ctxArgs } = await this.logCtx(args, this.deleteAll, true);
return this.repo.deleteAll(keys, ...ctxArgs);
}
async read(key, ...args) {
const { ctxArgs } = await this.logCtx(args, this.read, true);
return this.repo.read(key, ...ctxArgs);
}
async readAll(keys, ...args) {
const { ctxArgs } = await this.logCtx(args, this.readAll, true);
return this.repo.readAll(keys, ...ctxArgs);
}
async query(methodName, ...args) {
const method = this.repo?.[methodName];
if (typeof method !== "function")
throw new Error(`Method "${methodName}" is not implemented`);
return method.apply(this.repo, args);
}
async update(model, ...args) {
const { ctxArgs } = await this.logCtx(args, this.update, true);
return this.repo.update(model, ...ctxArgs);
}
async updateAll(models, ...args) {
const { ctxArgs } = await this.logCtx(args, this.updateAll, true);
return this.repo.updateAll(models, ...ctxArgs);
}
//
// async query(
// condition: Condition<M>,
// orderBy: keyof M,
// order: OrderDirection = OrderDirection.ASC,
// limit?: number,
// skip?: number,
// ...args: MaybeContextualArg<ContextOfRepository<R>>
// ): Promise<M[]> {
// const { ctxArgs } = await this.logCtx(args, this.query, true);
// return this.repo.query(condition, orderBy, order, limit, skip, ...ctxArgs);
// }
async listBy(key, order, ...args) {
const { ctxArgs } = await this.logCtx(args, this.listBy, true);
return this.repo.listBy(key, order, ...ctxArgs);
}
async paginateBy(key, order, ref, ...args) {
const { ctxArgs } = await this.logCtx(args, this.paginateBy, true);
return this.repo.paginateBy(key, order, ref, ...ctxArgs);
}
async findOneBy(key, value, ...args) {
const { ctxArgs } = await this.logCtx(args, this.findOneBy, true);
return this.repo.findOneBy(key, value, ...ctxArgs);
}
async findBy(key, value, ...args) {
const { ctxArgs } = await this.logCtx(args, this.findBy, true);
return this.repo.findBy(key, value, ...ctxArgs);
}
async statement(name, ...args) {
const { ctxArgs } = await this.logCtx(args, this.statement, true);
return this.repo.statement(name, ...ctxArgs);
}
async logCtx(args, method, allowCreate = false) {
return (await ModelService.logCtx.bind(this.repo["adapter"])(args, method, allowCreate, this.repo["_overrides"], this.class));
}
static forModel(model, alias) {
let instance;
alias = resolveAlias(alias || model);
try {
instance = ModelService.get(alias);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}
catch (e) {
instance = undefined;
}
if (instance instanceof ModelService)
return instance;
const Base = this;
let DecoratedService = class DecoratedService extends Base {
constructor() {
super(model);
}
};
DecoratedService = __decorate([
(0, decorators_1.service)(alias),
__metadata("design:paramtypes", [])
], DecoratedService);
return new DecoratedService();
}
static async logCtx(args, operation, allowCreate = false, overrides = {}, constructor) {
const bootCtx = async function bootCtx() {
if (!allowCreate)
throw new db_decorators_1.InternalError("No context provided");
return this.context(typeof operation === "string" ? operation : operation.name, overrides, constructor);
}.bind(this);
if (args.length < 1) {
args = [await bootCtx()];
}
let ctx = args.pop();
if (!(ctx instanceof Context_1.Context)) {
if (typeof ctx !== "undefined")
args.push(ctx);
ctx = (await bootCtx());
}
const log = (this
? ctx.logger.for(this).for(operation)
: ctx.logger.clear().for(this).for(operation));
return {
ctx: ctx,
log: log,
ctxArgs: [...args, ctx],
};
}
}
exports.ModelService = ModelService;
__decorate([
(0, decorators_1.create)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, void 0]),
__metadata("design:returntype", Promise)
], ModelService.prototype, "create", null);
__decorate([
(0, decorators_1.create)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Array, void 0]),
__metadata("design:returntype", Promise)
], ModelService.prototype, "createAll", null);
__decorate([
(0, decorators_1.del)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, void 0]),
__metadata("design:returntype", Promise)
], ModelService.prototype, "delete", null);
__decorate([
(0, decorators_1.del)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Array, void 0]),
__metadata("design:returntype", Promise)
], ModelService.prototype, "deleteAll", null);
__decorate([
(0, decorators_1.read)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, void 0]),
__metadata("design:returntype", Promise)
], ModelService.prototype, "read", null);
__decorate([
(0, decorators_1.read)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Array, void 0]),
__metadata("design:returntype", Promise)
], ModelService.prototype, "readAll", null);
__decorate([
(0, decorators_1.read)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object]),
__metadata("design:returntype", Promise)
], ModelService.prototype, "query", null);
__decorate([
(0, decorators_1.update)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, void 0]),
__metadata("design:returntype", Promise)
], ModelService.prototype, "update", null);
__decorate([
(0, decorators_1.update)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Array, Object]),
__metadata("design:returntype", Promise)
], ModelService.prototype, "updateAll", null);
//# sourceMappingURL=Services.js.map