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);
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
import { EncryptionSecret, WorkflowHostname } from "../EnvironmentConfig";
import PostgresAppInstance from "../Infrastructure/PostgresDatabase";
import ClusterKeyAuthorization from "../Middleware/ClusterKeyAuthorization";
import ModelPermission from "../Types/Database/Permissions/Index";
import QueryHelper from "../Types/Database/QueryHelper";
import Encryption from "../Utils/Encryption";
import logger from "../Utils/Logger";
import BaseService from "./BaseService";
import BaseModel from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
import { WorkflowRoute } from "../../ServiceRoute";
import Protocol from "../../Types/API/Protocol";
import Route from "../../Types/API/Route";
import URL from "../../Types/API/URL";
import SortOrder from "../../Types/BaseDatabase/SortOrder";
import { getMaxLengthFromTableColumnType } from "../../Types/Database/ColumnLength";
import LIMIT_MAX from "../../Types/Database/LimitMax";
import TableColumnType from "../../Types/Database/TableColumnType";
import { getUniqueColumnsBy } from "../../Types/Database/UniqueColumnBy";
import BadDataException from "../../Types/Exception/BadDataException";
import DatabaseNotConnectedException from "../../Types/Exception/DatabaseNotConnectedException";
import HashedString from "../../Types/HashedString";
import JSONFunctions from "../../Types/JSONFunctions";
import ObjectID from "../../Types/ObjectID";
import TelemetryContext from "../Utils/Telemetry/TelemetryContext";
import PositiveNumber from "../../Types/PositiveNumber";
import Text from "../../Types/Text";
import Typeof from "../../Types/Typeof";
import API from "../../Utils/API";
import Slug from "../../Utils/Slug";
import { EntityManager, } from "typeorm";
import Realtime from "../Utils/Realtime";
import ModelEventType from "../../Types/Realtime/ModelEventType";
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
class DatabaseService extends BaseService {
    get hardDeleteItemByColumnName() {
        return this._hardDeleteItemByColumnName;
    }
    set hardDeleteItemByColumnName(v) {
        this._hardDeleteItemByColumnName = v;
    }
    get hardDeleteItemsOlderThanDays() {
        return this._hardDeleteItemsOlderThanDays;
    }
    set hardDeleteItemsOlderThanDays(v) {
        this._hardDeleteItemsOlderThanDays = v;
    }
    constructor(modelType) {
        super();
        this._hardDeleteItemByColumnName = "";
        this._hardDeleteItemsOlderThanDays = 0;
        this.doNotAllowDelete = false;
        this.modelType = modelType;
        this.model = new modelType();
        this.modelName = modelType.name;
    }
    setDoNotAllowDelete(doNotAllowDelete) {
        this.doNotAllowDelete = doNotAllowDelete;
    }
    hardDeleteItemsOlderThanInDays(columnName, olderThan) {
        this.hardDeleteItemByColumnName = columnName;
        this.hardDeleteItemsOlderThanDays = olderThan;
    }
    getModel() {
        return this.model;
    }
    getQueryBuilder(modelName) {
        return this.getRepository().createQueryBuilder(modelName);
    }
    getRepository() {
        if (!PostgresAppInstance.isConnected()) {
            throw new DatabaseNotConnectedException();
        }
        const dataSource = PostgresAppInstance.getDataSource();
        if (dataSource) {
            return dataSource.getRepository(this.modelType.name);
        }
        throw new DatabaseNotConnectedException();
    }
    async executeTransaction(runInTransaction) {
        if (!PostgresAppInstance.isConnected()) {
            throw new DatabaseNotConnectedException();
        }
        const dataSource = PostgresAppInstance.getDataSource();
        if (!dataSource) {
            throw new DatabaseNotConnectedException();
        }
        return await dataSource.transaction(runInTransaction);
    }
    isValid(data) {
        if (!data) {
            throw new BadDataException("Data cannot be null");
        }
        return true;
    }
    generateDefaultValues(data) {
        const tableColumns = data.getTableColumns().columns;
        for (const column of tableColumns) {
            const metadata = data.getTableColumnMetadata(column);
            if (metadata.forceGetDefaultValueOnCreate) {
                data[column] = metadata.forceGetDefaultValueOnCreate();
            }
        }
        return data;
    }
    async checkForUniqueValues(data) {
        var _a;
        const tableColumns = data.getTableColumns().columns;
        for (const columnName of tableColumns) {
            const metadata = data.getTableColumnMetadata(columnName);
            if (metadata.unique && data.getColumnValue(columnName)) {
                // check for unique values.
                const count = await this.countBy({
                    query: {
                        [columnName]: data.getColumnValue(columnName),
                    },
                    props: {
                        isRoot: true,
                    },
                });
                if (count.toNumber() > 0) {
                    throw new BadDataException(`${metadata.title} ${(_a = data
                        .getColumnValue(columnName)) === null || _a === void 0 ? void 0 : _a.toString()} already exists. Please choose a different ${metadata.title}`);
                }
            }
        }
        return data;
    }
    checkRequiredFields(data) {
        // Check required fields.
        const relationalColumns = {};
        const tableColumns = data.getTableColumns().columns;
        for (const column of tableColumns) {
            const metadata = data.getTableColumnMetadata(column);
            if (metadata.manyToOneRelationColumn) {
                relationalColumns[metadata.manyToOneRelationColumn] = column;
            }
        }
        for (const requiredField of data.getRequiredColumns().columns) {
            if (typeof data[requiredField] === Typeof.Boolean) {
                if (!data[requiredField] &&
                    data[requiredField] !== false &&
                    !data.isDefaultValueColumn(requiredField)) {
                    throw new BadDataException(`${requiredField} is required`);
                }
            }
            else if (!data[requiredField] &&
                !data.isDefaultValueColumn(requiredField)) {
                const metadata = data.getTableColumnMetadata(requiredField);
                if (metadata &&
                    metadata.manyToOneRelationColumn &&
                    metadata.type === TableColumnType.Entity &&
                    data.getColumnValue(metadata.manyToOneRelationColumn)) {
                    continue;
                }
                if (relationalColumns[requiredField] &&
                    data.getColumnValue(relationalColumns[requiredField])) {
                    continue;
                }
                throw new BadDataException(`${requiredField} is required`);
            }
            else if (data[requiredField] === null &&
                data.isDefaultValueColumn(requiredField)) {
                delete data[requiredField];
            }
        }
        return data;
    }
    async onBeforeCreate(createBy) {
        // A place holder method used for overriding.
        return Promise.resolve({
            createBy: createBy,
            carryForward: undefined,
        });
    }
    async _onBeforeCreate(createBy) {
        // Private method that runs before create.
        const projectIdColumn = this.model.getTenantColumn();
        if (projectIdColumn && createBy.props.tenantId) {
            createBy.data[projectIdColumn] = createBy.props.tenantId;
        }
        return await this.onBeforeCreate(createBy);
    }
    async encrypt(data) {
        for (const key of this.model.getEncryptedColumns().columns) {
            if (!data[key]) {
                continue;
            }
            // If data is an object.
            if (typeof data[key] === Typeof.Object) {
                const dataObj = data[key];
                for (const key in dataObj) {
                    dataObj[key] = await Encryption.encrypt(dataObj[key]);
                }
                data[key] = dataObj;
            }
            else {
                //If its string or other type.
                data[key] = await Encryption.encrypt(data[key]);
            }
        }
        return data;
    }
    async hash(data) {
        const columns = data.getHashedColumns();
        for (const key of columns.columns) {
            if (data.hasValue(key) &&
                !data.getValue(key).isValueHashed()) {
                await data[key].hashValue(EncryptionSecret);
            }
        }
        return data;
    }
    async decrypt(data) {
        for (const key of data.getEncryptedColumns().columns) {
            if (!data.hasValue(key)) {
                continue;
            }
            // If data is an object.
            if (typeof data.getValue(key) === Typeof.Object) {
                const dataObj = data.getValue(key);
                for (const key in dataObj) {
                    dataObj[key] = await Encryption.decrypt(dataObj[key]);
                }
                data.setValue(key, dataObj);
            }
            else {
                //If its string or other type.
                data.setValue(key, await Encryption.decrypt(data[key]));
            }
        }
        return data;
    }
    async onBeforeDelete(deleteBy) {
        // A place holder method used for overriding.
        return Promise.resolve({ deleteBy, carryForward: null });
    }
    async onBeforeUpdate(updateBy) {
        // A place holder method used for overriding.
        return Promise.resolve({ updateBy, carryForward: null });
    }
    async onBeforeFind(findBy) {
        // A place holder method used for overriding.
        return Promise.resolve({ findBy, carryForward: null });
    }
    async onCreateSuccess(_onCreate, createdItem) {
        // A place holder method used for overriding.
        return Promise.resolve(createdItem);
    }
    async onCreateError(error) {
        // A place holder method used for overriding.
        return Promise.resolve(error);
    }
    async onUpdateSuccess(onUpdate, _updatedItemIds) {
        // A place holder method used for overriding.
        return Promise.resolve(onUpdate);
    }
    async onUpdateError(error) {
        // A place holder method used for overriding.
        return Promise.resolve(error);
    }
    async onDeleteSuccess(onDelete, _itemIdsBeforeDelete) {
        // A place holder method used for overriding.
        return Promise.resolve(onDelete);
    }
    async onDeleteError(error) {
        // A place holder method used for overriding.
        return Promise.resolve(error);
    }
    async onFindSuccess(onFind, items) {
        // A place holder method used for overriding.
        return Promise.resolve(Object.assign(Object.assign({}, onFind), { carryForward: items }));
    }
    async onFindError(error) {
        // A place holder method used for overriding.
        return Promise.resolve(error);
    }
    async onCountSuccess(count) {
        // A place holder method used for overriding.
        return Promise.resolve(count);
    }
    async onCountError(error) {
        // A place holder method used for overriding.
        return Promise.resolve(error);
    }
    async getException(error) {
        throw error;
    }
    generateSlug(createBy) {
        if (createBy.data.getSlugifyColumn()) {
            createBy.data[createBy.data.getSaveSlugToColumn()] =
                Slug.getSlug(createBy.data[createBy.data.getSlugifyColumn()]
                    ? createBy.data[createBy.data.getSlugifyColumn()]
                    : null);
        }
        return createBy;
    }
    async sanitizeCreateOrUpdate(data, props, isUpdate = false) {
        data = this.checkMaxLengthOfFields(data);
        const columns = this.model.getTableColumns();
        for (const columnName of columns.columns) {
            const tableColumnMetadata = this.model.getTableColumnMetadata(columnName);
            if (this.model.isEntityColumn(columnName)) {
                const columnValue = data[columnName];
                if (data &&
                    columnName &&
                    tableColumnMetadata.modelType &&
                    columnValue &&
                    tableColumnMetadata.type === TableColumnType.Entity &&
                    (typeof columnValue === "string" || columnValue instanceof ObjectID)) {
                    const relatedType = new tableColumnMetadata.modelType();
                    relatedType._id = columnValue.toString();
                    data[columnName] = relatedType;
                }
                if (data &&
                    Array.isArray(columnValue) &&
                    columnValue.length > 0 &&
                    tableColumnMetadata.modelType &&
                    columnValue &&
                    tableColumnMetadata.type === TableColumnType.EntityArray) {
                    const itemsArray = [];
                    for (const item of columnValue) {
                        if (typeof item === "string" || item instanceof ObjectID) {
                            const basemodelItem = new tableColumnMetadata.modelType();
                            basemodelItem._id = item.toString();
                            itemsArray.push(basemodelItem);
                        }
                        else if (item &&
                            typeof item === Typeof.Object &&
                            item["_id"] &&
                            typeof item["_id"] === Typeof.String) {
                            const basemodelItem = new tableColumnMetadata.modelType();
                            basemodelItem._id = item["_id"].toString();
                            itemsArray.push(basemodelItem);
                        }
                        else if (item &&
                            typeof item === Typeof.Object &&
                            item["id"] &&
                            typeof item["id"] === Typeof.String) {
                            const basemodelItem = new tableColumnMetadata.modelType();
                            basemodelItem._id = item["id"].toString();
                            itemsArray.push(basemodelItem);
                        }
                        else if (item instanceof BaseModel) {
                            itemsArray.push(item);
                        }
                    }
                    data[columnName] = itemsArray;
                }
            }
            if (this.model.isHashedStringColumn(columnName)) {
                const columnValue = data[columnName];
                if (data &&
                    columnName &&
                    columnValue &&
                    columnValue instanceof HashedString) {
                    if (!columnValue.isValueHashed()) {
                        await columnValue.hashValue(EncryptionSecret);
                    }
                    data[columnName] = columnValue.toString();
                }
            }
            // if its a Date column and if date is null then set it to null.
            if (data[columnName] === "" &&
                tableColumnMetadata.type === TableColumnType.Date) {
                data[columnName] = null;
            }
            // if table columntype is file and file is base64 stirng then convert to buffer to save.
            if (tableColumnMetadata.type === TableColumnType.File &&
                data[columnName] &&
                typeof data[columnName] === Typeof.String) {
                const fileBuffer = Buffer.from(data[columnName], "base64");
                data[columnName] = fileBuffer;
            }
        }
        // check createByUserId.
        if (!isUpdate && props.userId) {
            data["createdByUserId"] = props.userId;
        }
        return data;
    }
    async onTriggerRealtime(modelId, projectId, modelEventType) {
        logger.debug("Realtime Events Enabled", {
            projectId: projectId === null || projectId === void 0 ? void 0 : projectId.toString(),
        });
        logger.debug(this.model.enableRealtimeEventsOn, {
            projectId: projectId === null || projectId === void 0 ? void 0 : projectId.toString(),
        });
        if (Realtime.isInitialized() && this.model.enableRealtimeEventsOn) {
            logger.debug("Emitting realtime event", {
                projectId: projectId === null || projectId === void 0 ? void 0 : projectId.toString(),
            });
            let shouldEmitEvent = false;
            if (this.model.enableRealtimeEventsOn.create &&
                modelEventType === ModelEventType.Create) {
                shouldEmitEvent = true;
            }
            if (this.model.enableRealtimeEventsOn.update &&
                modelEventType === ModelEventType.Update) {
                shouldEmitEvent = true;
            }
            if (this.model.enableRealtimeEventsOn.delete &&
                modelEventType === ModelEventType.Delete) {
                shouldEmitEvent = true;
            }
            if (!shouldEmitEvent) {
                logger.debug("Realtime event not enabled for this event type", {
                    projectId: projectId === null || projectId === void 0 ? void 0 : projectId.toString(),
                });
                return;
            }
            logger.debug("Emitting realtime event", {
                projectId: projectId === null || projectId === void 0 ? void 0 : projectId.toString(),
            });
            Realtime.emitModelEvent({
                tenantId: projectId,
                eventType: modelEventType,
                modelId: modelId,
                modelType: this.modelType,
            }).catch((err) => {
                logger.error("Cannot emit realtime event", {
                    projectId: projectId === null || projectId === void 0 ? void 0 : projectId.toString(),
                });
                logger.error(err, {
                    projectId: projectId === null || projectId === void 0 ? void 0 : projectId.toString(),
                });
            });
        }
    }
    async onTriggerWorkflow(id, projectId, triggerType, miscData) {
        if (this.getModel().enableWorkflowOn) {
            API.post({
                url: new URL(Protocol.HTTP, WorkflowHostname, new Route(`/${WorkflowRoute.toString()}/model/${projectId.toString()}/${Text.pascalCaseToDashes(this.getModel().tableName)}/${triggerType}`)),
                data: {
                    data: {
                        _id: id.toString(),
                        miscData: miscData,
                    },
                },
                headers: Object.assign({}, ClusterKeyAuthorization.getClusterKeyHeaders()),
            }).catch((error) => {
                logger.error(error, {
                    projectId: projectId === null || projectId === void 0 ? void 0 : projectId.toString(),
                });
            });
        }
    }
    /**
     * Derive the telemetry attribute key for this model's primary id, e.g.
     * `Incident` -> `incidentId`, `Monitor` -> `monitorId`. Matches the keys in
     * TelemetryContextAttributes so dashboards/queries stay consistent.
     */
    getTelemetryEntityIdKey() {
        const name = this.modelName || "entity";
        return name.charAt(0).toLowerCase() + name.slice(1) + "Id";
    }
    /**
     * Seed the ambient telemetry context with the project (tenant) of an
     * operation so worker/cron/service spans and logs — which run outside the
     * HTTP request scope — still carry projectId. Best-effort and safe to call
     * anywhere.
     */
    setTelemetryContextFromProps(props) {
        try {
            if (props === null || props === void 0 ? void 0 : props.tenantId) {
                TelemetryContext.setAttributes({
                    projectId: props.tenantId.toString(),
                });
            }
        }
        catch (_a) {
            // Telemetry must never break a database operation.
        }
    }
    /**
     * Seed the ambient telemetry context from a concrete model instance: the
     * project (tenant) and the entity's own id (e.g. incidentId, monitorId).
     * Used on create, where a new entity id is minted. Best-effort and safe.
     */
    setTelemetryContextFromItem(item) {
        try {
            if (!item) {
                return;
            }
            const attributes = {};
            const tenantColumn = this.model.getTenantColumn();
            if (tenantColumn) {
                const tenantValue = item.getColumnValue(tenantColumn);
                if (tenantValue) {
                    attributes["projectId"] = String(tenantValue);
                }
            }
            if (item.id) {
                attributes[this.getTelemetryEntityIdKey()] = item.id.toString();
            }
            if (Object.keys(attributes).length > 0) {
                TelemetryContext.setAttributes(attributes);
            }
        }
        catch (_a) {
            // Telemetry must never break a database operation.
        }
    }
    async create(createBy) {
        var _a, _b;
        const onCreate = createBy.props.ignoreHooks
            ? { createBy, carryForward: [] }
            : await this._onBeforeCreate(createBy);
        let _createdBy = onCreate.createBy;
        const carryForward = onCreate.carryForward;
        _createdBy = this.generateSlug(_createdBy);
        let data = _createdBy.data;
        // add tenantId if present.
        const tenantColumnName = data.getTenantColumn();
        if (tenantColumnName && _createdBy.props.tenantId) {
            data.setColumnValue(tenantColumnName, _createdBy.props.tenantId);
        }
        data = this.generateDefaultValues(data);
        data = this.checkRequiredFields(data);
        await this.checkForUniqueValues(data);
        if (!this.isValid(data)) {
            throw new BadDataException("Data is not valid");
        }
        // check total items by.
        await this.checkTotalItemsBy(_createdBy);
        // Encrypt data
        data = (await this.encrypt(data));
        // hash data
        data = await this.hash(data);
        ModelPermission.checkCreatePermissions(this.modelType, data, _createdBy.props);
        createBy.data = data;
        // check uniqueColumns by:
        createBy = await this.checkUniqueColumnBy(createBy);
        // serialize.
        createBy.data = (await this.sanitizeCreateOrUpdate(createBy.data, createBy.props));
        try {
            createBy.data = await this.getRepository().save(createBy.data);
            // Seed telemetry context with projectId + <model>Id for this create.
            this.setTelemetryContextFromItem(createBy.data);
            if (!createBy.props.ignoreHooks) {
                createBy.data = await this.onCreateSuccess({
                    createBy,
                    carryForward,
                }, createBy.data);
            }
            /*
             * Auto-owner-on-create for @OperationalResource models. Inserts the
             * creating user into <Model>OwnerUser so the Owned permission scope
             * covers the newly-created resource on the next request. See
             * Internal/Docs/PermissionsSimplification.md. Best-effort: failures
             * are logged but do not roll back the create.
             */
            if (!createBy.props.ignoreHooks) {
                await this.autoOwnerOnCreate(createBy.data, createBy.props);
            }
            let tenantId = createBy.props.tenantId;
            if (!tenantId && this.getModel().getTenantColumn()) {
                tenantId = createBy.data.getValue(this.getModel().getTenantColumn());
            }
            // hit workflow.;
            if (((_a = this.getModel().enableWorkflowOn) === null || _a === void 0 ? void 0 : _a.create) && tenantId) {
                await this.onTriggerWorkflow(createBy.data.id, tenantId, "on-create");
            }
            if (tenantId) {
                await this.onTriggerRealtime(createBy.data.id, tenantId, ModelEventType.Create);
            }
            if (!createBy.props.ignoreHooks &&
                ((_b = this.getModel().enableAuditLogOn) === null || _b === void 0 ? void 0 : _b.create)) {
                /*
                 * Lazy require to avoid circular dependency between DatabaseService and
                 * AuditLogService (which depends on ProjectService/UserService, both of
                 * which extend DatabaseService). A top-level import leaves
                 * DatabaseService undefined at class-extension time for subclasses.
                 */
                const auditLogService = 
                // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
                require("./AuditLogService").default;
                await auditLogService.recordCreate({
                    model: this.getModel(),
                    createdItem: createBy.data,
                    props: createBy.props,
                });
            }
            return createBy.data;
        }
        catch (error) {
            await this.onCreateError(error);
            throw this.getException(error);
        }
    }
    /*
     * Inserts the creating user into the resource's *OwnerUser table for
     * operational resources. Mirrors the existing OwnerRule behavior for user
     * assignment and gives the creator immediate access under the `Owned`
     * permission scope.
     */
    async autoOwnerOnCreate(createdItem, props) {
        /*
         * System/root creates don't get an owner; non-user callers (API keys,
         * Probes) have no userId either. Both evaluate Owned as All elsewhere.
         */
        if (props.isRoot || props.isMasterAdmin || !props.userId) {
            return;
        }
        // The @OperationalResource() decorator sets this on the model prototype.
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        if (!createdItem.isOperationalResource) {
            return;
        }
        const modelName = this.modelType.name;
        /*
         * Lazy require to avoid circular dependency: owner services extend
         * DatabaseService, so importing them at top-level leaves DatabaseService
         * undefined at class-extension time.
         */
        const ownerTableRegistry = 
        // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
        require("../Types/Database/Permissions/OwnerTableRegistry").default;
        const entry = ownerTableRegistry.get(modelName);
        if (!entry) {
            /*
             * Operational but no registered owner tables — not a configuration we
             * know how to auto-own. Skip silently.
             */
            return;
        }
        const resourceId = createdItem.id || undefined;
        if (!resourceId) {
            logger.error(`auto-owner-on-create: created ${modelName} has no id; skipping`);
            return;
        }
        const tenantColumnName = createdItem.getTenantColumn();
        let projectId = undefined;
        if (tenantColumnName) {
            projectId =
                createdItem.getValue(tenantColumnName) || props.tenantId;
        }
        else {
            projectId = props.tenantId;
        }
        if (!projectId) {
            logger.error(`auto-owner-on-create: no projectId for ${modelName} ${resourceId.toString()}; skipping`);
            return;
        }
        try {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            const ownerModel = entry.ownerUserService.getModel();
            ownerModel[entry.fkColumn] = resourceId;
            ownerModel.userId = props.userId;
            ownerModel.projectId = projectId;
            await entry.ownerUserService.create({
                data: ownerModel,
                props: { isRoot: true },
            });
        }
        catch (err) {
            logger.error(`auto-owner-on-create failed for ${modelName} ${resourceId.toString()}`);
            logger.error(err);
        }
    }
    checkMaxLengthOfFields(data) {
        // Check required fields.
        const tableColumns = this.model.getTableColumns().columns;
        for (const column of tableColumns) {
            const metadata = this.model.getTableColumnMetadata(column);
            if (data[column] &&
                metadata.type &&
                getMaxLengthFromTableColumnType(metadata.type)) {
                if (data[column].toString().length >
                    getMaxLengthFromTableColumnType(metadata.type)) {
                    throw new BadDataException(`${column} length cannot be more than ${getMaxLengthFromTableColumnType(metadata.type)} characters`);
                }
            }
        }
        return data;
    }
    async checkTotalItemsBy(createdBy) {
        const totalItemsColumnName = this.model.getTotalItemsByColumnName();
        const totalItemsNumber = this.model.getTotalItemsNumber();
        const errorMessage = this.model.getTotalItemsByErrorMessage();
        if (totalItemsColumnName &&
            totalItemsNumber &&
            errorMessage &&
            createdBy.data.getColumnValue(totalItemsColumnName)) {
            const count = await this.countBy({
                query: {
                    [totalItemsColumnName]: createdBy.data.getColumnValue(totalItemsColumnName),
                },
                skip: 0,
                limit: LIMIT_MAX,
                props: {
                    isRoot: true,
                },
            });
            if (count.positiveNumber > totalItemsNumber - 1) {
                throw new BadDataException(errorMessage);
            }
        }
    }
    async checkUniqueColumnBy(createBy) {
        let existingItemsWithSameNameCount = 0;
        const uniqueColumnsBy = getUniqueColumnsBy(createBy.data);
        for (const key in uniqueColumnsBy) {
            if (!uniqueColumnsBy[key]) {
                continue;
            }
            if (typeof uniqueColumnsBy[key] === Typeof.String) {
                uniqueColumnsBy[key] = [uniqueColumnsBy[key]];
            }
            const query = {};
            for (const uniqueByColumnName of uniqueColumnsBy[key]) {
                const columnValue = createBy.data[uniqueByColumnName];
                if (columnValue === null || columnValue === undefined) {
                    query[uniqueByColumnName] = QueryHelper.isNull();
                }
                else {
                    query[uniqueByColumnName] = columnValue;
                }
            }
            existingItemsWithSameNameCount = (await this.countBy({
                query: Object.assign({ [key]: QueryHelper.findWithSameText(createBy.data[key]
                        ? createBy.data[key]
                        : "") }, query),
                props: {
                    isRoot: true,
                },
            })).toNumber();
            if (existingItemsWithSameNameCount > 0) {
                throw new BadDataException(`${this.model.singularName} with the same ${key} already exists.`);
            }
            existingItemsWithSameNameCount = 0;
        }
        return Promise.resolve(createBy);
    }
    async countBy({ query, skip, limit, props, groupBy, distinctOn, }) {
        try {
            if (groupBy && Object.keys(groupBy).length > 0) {
                throw new BadDataException("Group By is not supported for countBy");
            }
            if (!skip) {
                skip = new PositiveNumber(0);
            }
            if (!limit) {
                limit = new PositiveNumber(Infinity);
            }
            if (!(skip instanceof PositiveNumber)) {
                skip = new PositiveNumber(skip);
            }
            if (!(limit instanceof PositiveNumber)) {
                limit = new PositiveNumber(limit);
            }
            const findBy = {
                query,
                skip,
                limit,
                props,
            };
            const checkReadPermissionType = await ModelPermission.checkReadQueryPermission(this.modelType, query, null, props);
            findBy.query = checkReadPermissionType.query;
            let count = 0;
            if (distinctOn) {
                const queryBuilder = this.getQueryBuilder(this.modelName)
                    .where(findBy.query)
                    .skip(skip.toNumber())
                    .take(limit.toNumber());
                if (distinctOn) {
                    queryBuilder.groupBy(`${this.modelName}.${distinctOn}`);
                }
                count = await queryBuilder.getCount();
            }
            else {
                count = await this.getRepository().count({
                    where: findBy.query,
                    skip: findBy.skip.toNumber(),
                    take: findBy.limit.toNumber(),
                });
            }
            let countPositive = new PositiveNumber(count);
            countPositive = await this.onCountSuccess(countPositive);
            return countPositive;
        }
        catch (error) {
            await this.onCountError(error);
            throw this.getException(error);
        }
    }
    async deleteOneById(deleteById) {
        await ModelPermission.checkDeletePermissionByModel({
            modelType: this.modelType,
            fetchModelWithAccessControlIds: async () => {
                const selectModel = {};
                const accessControlColumn = this.getModel().getAccessControlColumn();
                if (accessControlColumn) {
                    selectModel[accessControlColumn] = {
                        _id: true,
                        name: true,
                    };
                }
                return await this.findOneById({
                    id: deleteById.id,
                    select: selectModel,
                    props: {
                        isRoot: true,
                    },
                });
            },
            props: deleteById.props,
        });
        return await this.deleteOneBy({
            query: {
                _id: deleteById.id.toString(),
            },
            deletedByUser: deleteById.deletedByUser,
            props: deleteById.props,
        });
    }
    async deleteOneBy(deleteOneBy) {
        return await this._deleteBy(Object.assign(Object.assign({}, deleteOneBy), { limit: 1, skip: 0 }));
    }
    async deleteBy(deleteBy) {
        return await this._deleteBy(deleteBy);
    }
    async hardDeleteBy(deleteBy) {
        try {
            const onDelete = deleteBy.props.ignoreHooks
                ? { deleteBy, carryForward: [] }
                : await this.onBeforeDelete(deleteBy);
            const beforeDeleteBy = onDelete.deleteBy;
            beforeDeleteBy.query = await ModelPermission.checkDeleteQueryPermission(this.modelType, beforeDeleteBy.query, deleteBy.props);
            if (!(beforeDeleteBy.skip instanceof PositiveNumber)) {
                beforeDeleteBy.skip = new PositiveNumber(beforeDeleteBy.skip);
            }
            if (!(beforeDeleteBy.limit instanceof PositiveNumber)) {
                beforeDeleteBy.limit = new PositiveNumber(beforeDeleteBy.limit);
            }
            const items = await this._findBy({
                query: beforeDeleteBy.query,
                skip: beforeDeleteBy.skip.toNumber(),
                limit: beforeDeleteBy.limit.toNumber(),
                select: {},
                props: Object.assign(Object.assign({}, beforeDeleteBy.props), { ignoreHooks: true }),
            }, true);
            let numberOfDocsAffected = 0;
            if (items.length > 0) {
                beforeDeleteBy.query = Object.assign(Object.assign({}, beforeDeleteBy.query), { _id: QueryHelper.any(items.map((i) => {
                        return i.id;
                    })) });
                numberOfDocsAffected =
                    (await this.getRepository().delete(beforeDeleteBy.query))
                        .affected || 0;
            }
            return numberOfDocsAffected;
        }
        catch (error) {
            await this.onDeleteError(error);
            throw this.getException(error);
        }
    }
    async _deleteBy(deleteBy) {
        var _a, _b, _c, _d;
        try {
            this.setTelemetryContextFromProps(deleteBy.props);
            if (this.doNotAllowDelete && !deleteBy.props.isRoot) {
                throw new BadDataException("Delete not allowed");
            }
            const onDelete = deleteBy.props.ignoreHooks
                ? { deleteBy, carryForward: [] }
                : await this.onBeforeDelete(deleteBy);
            const beforeDeleteBy = onDelete.deleteBy;
            const carryForward = onDelete.carryForward;
            beforeDeleteBy.query = await ModelPermission.checkDeleteQueryPermission(this.modelType, beforeDeleteBy.query, deleteBy.props);
            if (!(beforeDeleteBy.skip instanceof PositiveNumber)) {
                beforeDeleteBy.skip = new PositiveNumber(beforeDeleteBy.skip);
            }
            if (!(beforeDeleteBy.limit instanceof PositiveNumber)) {
                beforeDeleteBy.limit = new PositiveNumber(beforeDeleteBy.limit);
            }
            const select = {};
            if (this.getModel().getTenantColumn()) {
                select[this.getModel().getTenantColumn()] = true;
            }
            /*
             * If audit logging on delete is enabled, fetch all scalar columns so we
             * can record a full snapshot of the record before it is deleted.
             */
            if ((_a = this.getModel().enableAuditLogOn) === null || _a === void 0 ? void 0 : _a.delete) {
                const allColumns = this.getModel().getTableColumns().columns;
                for (const columnName of allColumns) {
                    select[columnName] = true;
                }
            }
            const items = await this._findBy({
                query: beforeDeleteBy.query,
                skip: beforeDeleteBy.skip.toNumber(),
                limit: beforeDeleteBy.limit.toNumber(),
                select: select,
                props: {
                    isRoot: true, // isRoot because query has already been checked for permissions.
                    ignoreHooks: true,
                },
            });
            /*
             * We are hard deleting anyway. So, this does not make sense. Please uncomment if
             * we change the code to soft-delete.
             */
            /*
             * await this._updateBy({
             *     query: deleteBy.query,
             *     data: {
             *         deletedByUserId: deleteBy.props.userId,
             *     } as any,
             *     limit: deleteBy.limit,
             *     skip: deleteBy.skip,
             *     props: {
             *         isRoot: true,
             *         ignoreHooks: true,
             *     },
             * });
             */
            let numberOfDocsAffected = 0;
            if (items.length > 0) {
                const query = {
                    _id: QueryHelper.any(items.map((i) => {
                        return i.id;
                    })),
                };
                numberOfDocsAffected =
                    (await this.getRepository().delete(query)).affected || 0;
            }
            // hit workflow.
            if (((_b = this.getModel().enableWorkflowOn) === null || _b === void 0 ? void 0 : _b.delete) &&
                (deleteBy.props.tenantId || this.getModel().getTenantColumn())) {
                for (const item of items) {
                    if ((_c = this.getModel().enableWorkflowOn) === null || _c === void 0 ? void 0 : _c.create) {
                        let tenantId = deleteBy.props.tenantId;
                        if (!tenantId && this.getModel().getTenantColumn()) {
                            tenantId = item.getValue(this.getModel().getTenantColumn());
                        }
                        if (tenantId) {
                            await this.onTriggerWorkflow(item.id, tenantId, "on-delete");
                            await this.onTriggerRealtime(item.id, tenantId, ModelEventType.Delete);
                        }
                    }
                }
            }
            if (!deleteBy.props.ignoreHooks) {
                await this.onDeleteSuccess({ deleteBy, carryForward }, items.map((i) => {
                    return new ObjectID(i._id);
                }));
            }
            if (((_d = this.getModel().enableAuditLogOn) === null || _d === void 0 ? void 0 : _d.delete) && items.length > 0) {
                const auditLogService = 
                // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
                require("./AuditLogService").default;
                for (const item of items) {
                    if (item.id) {
                        await auditLogService.recordDelete({
                            model: this.getModel(),
                            deletedItem: item,
                            itemId: item.id,
                            props: deleteBy.props,
                        });
                    }
                }
            }
            return numberOfDocsAffected;
        }
        catch (error) {
            await this.onDeleteError(error);
            throw this.getException(error);
        }
    }
    async findAllBy(findAllBy) {
        const { limit, skip } = findAllBy, rest = __rest(findAllBy, ["limit", "skip"]);
        let remaining = this.normalizePositiveNumber(limit);
        let currentSkip = this.normalizePositiveNumber(skip) || 0;
        const results = [];
        while (true) {
            const currentBatchSize = remaining !== undefined
                ? Math.min(LIMIT_MAX, Math.max(remaining, 0))
                : LIMIT_MAX;
            if (currentBatchSize <= 0) {
                break;
            }
            const page = await this.findBy(Object.assign(Object.assign({}, rest), { skip: currentSkip, limit: currentBatchSize }));
            if (page.length === 0) {
                break;
            }
            results.push(...page);
            currentSkip += page.length;
            if (remaining !== undefined) {
                remaining -= page.length;
                if (remaining <= 0) {
                    break;
                }
            }
            if (page.length < currentBatchSize) {
                break;
            }
        }
        return results;
    }
    normalizePositiveNumber(value) {
        if (value === undefined || value === null) {
            return undefined;
        }
        if (value instanceof PositiveNumber) {
            return value.toNumber();
        }
        if (typeof value === "number") {
            return value;
        }
        return undefined;
    }
    async findBy(findBy) {
        return await this._findBy(findBy);
    }
    async _findBy(findBy, withDeleted) {
        try {
            this.setTelemetryContextFromProps(findBy.props);
            let automaticallyAddedCreatedAtInSelect = false;
            if (!findBy.sort || Object.keys(findBy.sort).length === 0) {
                findBy.sort = {
                    createdAt: SortOrder.Descending,
                };
                if (!findBy.select) {
                    findBy.select = {};
                }
                if (!findBy.select["createdAt"]) {
                    findBy.select["createdAt"] = true;
                    automaticallyAddedCreatedAtInSelect = true;
                }
            }
            const onFind = findBy.props.ignoreHooks
                ? { findBy, carryForward: [] }
                : await this.onBeforeFind(findBy);
            const onBeforeFind = Object.assign({}, onFind.findBy);
            const carryForward = onFind.carryForward;
            if (!onBeforeFind.select ||
                Object.keys(onBeforeFind.select).length === 0) {
                onBeforeFind.select = {};
            }
            if (!onBeforeFind.select["_id"]) {
                onBeforeFind.select["_id"] = true;
            }
            const result = await ModelPermission.checkReadQueryPermission(this.modelType, onBeforeFind.query, onBeforeFind.select || null, onBeforeFind.props);
            onBeforeFind.query = result.query;
            onBeforeFind.select = result.select || undefined;
            if (!(onBeforeFind.skip instanceof PositiveNumber)) {
                onBeforeFind.skip = new PositiveNumber(onBeforeFind.skip);
            }
            if (!(onBeforeFind.limit instanceof PositiveNumber)) {
                onBeforeFind.limit = new PositiveNumber(onBeforeFind.limit);
            }
            if (onBeforeFind.groupBy &&
                Object.keys(onBeforeFind.groupBy).length > 0) {
                throw new BadDataException("GroupBy is currently not supported");
            }
            const items = await this.getRepository().find({
                skip: onBeforeFind.skip.toNumber(),
                take: onBeforeFind.limit.toNumber(),
                where: onBeforeFind.query,
                order: onBeforeFind.sort,
                relations: result.relationSelect,
                select: onBeforeFind.select,
                withDeleted: withDeleted || false,
            });
            let decryptedItems = [];
            for (const item of items) {
                decryptedItems.push(await this.decrypt(item));
            }
            decryptedItems = this.sanitizeFindByItems(decryptedItems, onBeforeFind);
            for (const item of decryptedItems) {
                if (automaticallyAddedCreatedAtInSelect) {
                    delete item.createdAt;
                }
            }
            if (!findBy.props.ignoreHooks) {
                decryptedItems = await (await this.onFindSuccess({ findBy, carryForward }, decryptedItems)).carryForward;
            }
            return decryptedItems;
        }
        catch (error) {
            await this.onFindError(error);
            throw this.getException(error);
        }
    }
    sanitizeFindByItems(items, findBy) {
        // if there's no select then there's nothing to do.
        if (!findBy.select) {
            return items;
        }
        for (const key in findBy.select) {
            // for each key in select check if there's nested properties, this indicates there's a relation.
            if (typeof findBy.select[key] === Typeof.Object) {
                // get meta data to check if this column is an entity array.
                const tableColumnMetadata = this.model.getTableColumnMetadata(key);
                if (!tableColumnMetadata.modelType) {
                    throw new BadDataException("Select not supported on " +
                        key +
                        " of " +
                        this.model.singularName +
                        " because this column modelType is not found.");
                }
                const relatedModel = new tableColumnMetadata.modelType();
                if (tableColumnMetadata.type === TableColumnType.EntityArray) {
                    const tableColumns = relatedModel.getTableColumns().columns;
                    const columnsToKeep = Object.keys(findBy.select[key]);
                    for (const item of items) {
                        if (item[key] && Array.isArray(item[key])) {
                            const relatedArray = item[key];
                            const newArray = [];
                            // now we need to sanitize data.
                            for (const relatedArrayItem of relatedArray) {
                                for (const column of tableColumns) {
                                    if (!columnsToKeep.includes(column)) {
                                        relatedArrayItem[column] = undefined;
                                    }
                                }
                                newArray.push(relatedArrayItem);
                            }
                            item[key] = newArray;
                        }
                    }
                }
            }
        }
        return items;
    }
    async findOneBy(findOneBy) {
        const findBy = findOneBy;
        findBy.limit = new PositiveNumber(1);
        findBy.skip = new PositiveNumber(0);
        const documents = await this._findBy(findBy);
        if (documents && documents[0]) {
            return documents[0];
        }
        return null;
    }
    async findOneById(findOneById) {
        if (!findOneById.id) {
            throw new BadDataException("findOneById.id is required");
        }
        return await this.findOneBy({
            query: {
                _id: findOneById.id.toString(),
            },
            select: findOneById.select || {},
            props: findOneById.props,
        });
    }
    async _updateBy(updateBy) {
        var _a, _b, _c, _d, _e;
        try {
            this.setTelemetryContextFromProps(updateBy.props);
            const onUpdate = updateBy.props.ignoreHooks
                ? { updateBy, carryForward: [] }
                : await this.onBeforeUpdate(updateBy);
            // Encrypt data
            updateBy.data = (await this.encrypt(updateBy.data));
            const beforeUpdateBy = onUpdate.updateBy;
            const carryForward = onUpdate.carryForward;
            beforeUpdateBy.query = await ModelPermission.checkUpdateQueryPermissions(this.modelType, beforeUpdateBy.query, beforeUpdateBy.data, beforeUpdateBy.props);
            const data = (await this.sanitizeCreateOrUpdate(beforeUpdateBy.data, updateBy.props, true));
            if (!(updateBy.skip instanceof PositiveNumber)) {
                updateBy.skip = new PositiveNumber(updateBy.skip);
            }
            if (!(updateBy.limit instanceof PositiveNumber)) {
                updateBy.limit = new PositiveNumber(updateBy.limit);
            }
            const dataColumns = [];
            const dataKeys = Object.keys(data);
            for (const key of dataKeys) {
                dataColumns.push([key, true]);
            }
            /*
             * Select the `_id` column and the columns in `data`.
             * `_id` is used for locating database records for updates, and `data`
             * columns are used for checking if the update causes a change in values.
             */
            const selectColumns = Object.assign({ _id: true }, Object.fromEntries(dataColumns));
            if (this.getModel().getTenantColumn()) {
                selectColumns[this.getModel().getTenantColumn().toString()] =
                    true;
            }
            /*
             * When audit logging on update is enabled, ensure the resource's display
             * name is loaded on the `before` snapshot so the audit entry records the
             * human-readable resource name even when the update doesn't touch it.
             */
            if ((_a = this.getModel().enableAuditLogOn) === null || _a === void 0 ? void 0 : _a.update) {
                const nameCandidates = [
                    "name",
                    "title",
                    "displayName",
                ];
                const modelColumns = this.getModel().getTableColumns().columns;
                for (const candidate of nameCandidates) {
                    if (modelColumns.includes(candidate)) {
                        selectColumns[candidate] = true;
                    }
                }
            }
            const items = await this._findBy({
                query: beforeUpdateBy.query,
                skip: updateBy.skip.toNumber(),
                limit: updateBy.limit.toNumber(),
                select: selectColumns,
                props: { isRoot: true, ignoreHooks: true },
            });
            for (const item of items) {
                const updatedItem = Object.assign({ _id: item._id }, data);
                logger.debug("Updated Item", {
                    projectId: (_b = updateBy.props.tenantId) === null || _b === void 0 ? void 0 : _b.toString(),
                });
                logger.debug(JSON.stringify(updatedItem, null, 2), {
                    projectId: (_c = updateBy.props.tenantId) === null || _c === void 0 ? void 0 : _c.toString(),
                });
                await this.getRepository().save(updatedItem);
                // hit workflow.
                if (((_d = this.getModel().enableWorkflowOn) === null || _d === void 0 ? void 0 : _d.update) &&
                    // Only trigger workflow if there's a change in values
                    !this.hasSameValues({ item, updatedItem })) {
                    let tenantId = updateBy.props.tenantId;
                    if (!tenantId && this.getModel().getTenantColumn()) {
                        tenantId = item.getValue(this.getModel().getTenantColumn());
                    }
                    if (tenantId) {
                        await this.onTriggerWorkflow(item.id, tenantId, "on-update", {
                            updatedFields: JSONFunctions.serialize(data),
                        });
                        await this.onTriggerRealtime(item.id, tenantId, ModelEventType.Update);
                    }
                }
                if (((_e = this.getModel().enableAuditLogOn) === null || _e === void 0 ? void 0 : _e.update) &&
                    !this.hasSameValues({ item, updatedItem }) &&
                    item.id) {
                    const auditLogService = 
                    // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
                    require("./AuditLogService").default;
                    await auditLogService.recordUpdate({
                        model: this.getModel(),
                        before: item,
                        updatedFields: data,
                        itemId: item.id,
                        props: updateBy.props,
                    });
                }
            }
            /*
             * Cant Update relations.
             * https://github.com/typeorm/typeorm/issues/2821
             */
            /*
             * const numberOfDocsAffected: number =
             *     (
             *         await this.getRepository().update(
             *             query as any,
             *             data
             *         )
             *     ).affected || 0;
             */
            if (!updateBy.props.ignoreHooks) {
                await this.onUpdateSuccess({ updateBy, carryForward }, items.map((i) => {
                    return new ObjectID(i._id);
                }));
            }
            return items.length;
        }
        catch (error) {
            await this.onUpdateError(error);
            throw this.getException(error);
        }
    }
    hasSameValues(data) {
        var _a, _b;
        const { item, updatedItem } = data;
        const columns = Object.keys(updatedItem);
        for (const column of columns) {
            if (
            /*
             * `toString()` is necessary so we can compare wrapped values
             * (e.g. `ObjectID`) with raw values (e.g. `string`)
             */
            ((_a = item.getColumnValue(column)) === null || _a === void 0 ? void 0 : _a.toString()) !==
                ((_b = updatedItem[column]) === null || _b === void 0 ? void 0 : _b.toString())) {
                return false;
            }
        }
        return true;
    }
    async updateOneBy(updateOneBy) {
        return await this._updateBy(Object.assign(Object.assign({}, updateOneBy), { limit: 1, skip: 0 }));
    }
    async updateBy(updateBy) {
        return await this._updateBy(updateBy);
    }
    async updateOneById(updateById) {
        if (!updateById.id) {
            throw new BadDataException("updateById.id is required");
        }
        await ModelPermission.checkUpdatePermissionByModel({
            modelType: this.modelType,
            fetchModelWithAccessControlIds: async () => {
                const selectModel = {};
                const accessControlColumn = this.getModel().getAccessControlColumn();
                if (accessControlColumn) {
                    selectModel[accessControlColumn] = {
                        _id: true,
                        name: true,
                    };
                }
                return await this.findOneById({
                    id: updateById.id,
                    select: selectModel,
                    props: {
                        isRoot: true,
                    },
                });
            },
            props: updateById.props,
        });
        await this.updateOneBy({
            query: {
                _id: updateById.id.toString(),
            },
            data: updateById.data,
            props: updateById.props,
        });
    }
    async updateOneByIdAndFetch(updateById) {
        await this.updateOneById(updateById);
        return this.findOneById({
            id: updateById.id,
            select: updateById.select,
            props: updateById.props,
        });
    }
    async atomicIncrementColumnValueByOne(data) {
        await this.getRepository().increment({ _id: data.id.toString() }, data.columnName, 1);
    }
    async searchBy({ skip, limit, select, props, }) {
        const query = {};
        // query[column] = RegExp(`^${text}`, 'i');
        const [items, count] = await Promise.all([
            this.findBy({
                query,
                skip,
                limit,
                select,
                props: props,
            }),
            this.countBy({
                query,
                skip: new PositiveNumber(0),
                limit: new PositiveNumber(Infinity),
                props: props,
            }),
        ]);
        return { items, count };
    }
}
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID,
        ObjectID, String]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "onTriggerRealtime", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID,
        ObjectID, String, Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "onTriggerWorkflow", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "create", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "countBy", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "deleteOneById", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "deleteOneBy", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "deleteBy", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "hardDeleteBy", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "findAllBy", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "findBy", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "findOneBy", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "findOneById", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "updateOneBy", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "updateBy", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "updateOneById", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "updateOneByIdAndFetch", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "atomicIncrementColumnValueByOne", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], DatabaseService.prototype, "searchBy", null);
export default DatabaseService;
export { EntityManager };
//# sourceMappingURL=DatabaseService.js.map