UNPKG

@decaf-ts/core

Version:

Core persistence module for the decaf framework

391 lines 15.7 kB
"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