UNPKG

@just-in/core

Version:

A TypeScript-first framework for building adaptive digital health interventions.

365 lines 13.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TestingMongoDBManager = exports.MongoDBManager = void 0; const mongoDB = __importStar(require("mongodb")); const stream_1 = require("stream"); const data_manager_type_1 = require("../data-manager.type"); const data_manager_constants_1 = require("../data-manager.constants"); const logger_manager_1 = require("../../logger/logger-manager"); const data_manager_helpers_1 = require("../data-manager.helpers"); const mongo_helpers_1 = require("./mongo.helpers"); const DEFAULT_MONGO_URI = "mongodb://127.0.0.1:27017?retryWrites=true&w=majority"; const DEFAULT_DB_NAME = "justin"; let _db; let _client; let _isConnected = false; /** * Sets the MongoDB database instance. * @returns void */ const _setDatabaseInstance = (db) => { _db = db; }; /** * Sets the MongoDB client instance. * @returns void */ const _setClient = (client) => { _client = client; }; /** * Sets the MongoDB database connection status. * @returns void */ const _setIsConnected = (isConnected) => { _isConnected = isConnected; }; /** * Initializes the MongoDB connection. * Establishes a connection to MongoDB using environment variables or default values. * @returns A Promise that resolves when the connection is established. * @throws Will throw an error if the connection fails. */ const init = async () => { if (_isConnected) return; logger_manager_1.Log.dev(`In mongo-data-manager.ts, MONGO_URI: ${process.env.MONGO_URI}`); const uri = process.env.MONGO_URI || DEFAULT_MONGO_URI; const dbName = process.env.DB_NAME || DEFAULT_DB_NAME; _client = new mongoDB.MongoClient(uri); try { await _client.connect(); logger_manager_1.Log.dev(`MongoDBManager connected to ${uri}`); _isConnected = true; _db = _client.db(dbName); logger_manager_1.Log.dev(`MongoDBManager initialized with database ${dbName}`); } catch (error) { logger_manager_1.Log.error("Failed to connect to MongoDB", error); throw error; } }; /** * Closes the MongoDB connection. * Ensures the MongoDB connection is initialized, then attempts to close it. * @returns A Promise that resolves when the connection is closed. * @throws Will throw an error if closing the connection fails. */ const close = async () => { ensureInitialized(); try { await _client.close(); _isConnected = false; logger_manager_1.Log.dev('MongoDBManager MongoDB client connection closed'); } catch (error) { (0, data_manager_helpers_1.handleDbError)("Error closing MongoDBManager connection", error); } }; /** * Ensures the MongoDB connection is initialized. * @throws Will throw an error if the MongoDB client is not initialized. */ const ensureInitialized = () => { // print all three variables logger_manager_1.Log.dev(`MongoDBManager ensureInitialized: _client: ${_client}, _isConnected: ${_isConnected}, _db: ${_db}`); if (!_client || !_isConnected || !_db) { const errorMessage = "MongoDB client not initialized"; logger_manager_1.Log.error(errorMessage); throw new Error(errorMessage); } }; /** * Transforms a MongoDB document by replacing `_id` with `id`. * @param {any} doc - The document to transform. * @returns {object | null} The transformed document with `id` instead of `_id` or null if . */ const transformId = (doc) => { if (!doc) return null; const { _id, ...rest } = doc; return { ...rest, id: (_id === null || _id === void 0 ? void 0 : _id.toString()) || data_manager_constants_1.NO_ID }; }; /** * Creates a readable stream to monitor collection changes. * @param collectionName - The name of the collection to monitor. * @param changeType - The type of change to monitor (insert, update, or delete). * @returns A readable stream of collection changes. */ const getCollectionChangeReadable = (collectionName, changeType) => { ensureInitialized(); const filterList = [{ $match: { operationType: changeType } }]; const options = changeType === data_manager_type_1.CollectionChangeType.UPDATE ? { fullDocument: "updateLookup" } : {}; const changeStream = _db .collection(collectionName) .watch(filterList, options); const collectionChangeReadable = new stream_1.Readable({ objectMode: true }); collectionChangeReadable._read = () => { }; const handleStreamClose = () => { logger_manager_1.Log.info(`Change stream for ${collectionName} closed`); collectionChangeReadable.destroy(); }; const handleStreamError = (error) => { logger_manager_1.Log.error("Change stream error", error); collectionChangeReadable.emit("error", error); throw error; }; const pushToStream = (nextDoc) => { var _a; let normalizedDoc; if (changeType === data_manager_type_1.CollectionChangeType.DELETE) { normalizedDoc = { id: ((_a = nextDoc.documentKey._id) === null || _a === void 0 ? void 0 : _a.toString()) || data_manager_constants_1.NO_ID, }; } else { normalizedDoc = transformId(nextDoc.fullDocument); } logger_manager_1.Log.dev(`Pushing to stream: ${JSON.stringify(normalizedDoc)}`); collectionChangeReadable.push(normalizedDoc); }; changeStream.on("change", pushToStream); changeStream.on("close", handleStreamClose); changeStream.on("error", handleStreamError); collectionChangeReadable.on("close", () => changeStream.close()); return collectionChangeReadable; }; /** * Inserts an item into a specified collection. * @param collectionName - The name of the collection. * @param obj - The object to insert into the collection. * @returns A Promise resolving with the ID of the inserted item. */ const addItemToCollection = async (collectionName, obj) => { ensureInitialized(); const { id, _id, ...filteredObject } = obj; try { const result = await _db .collection(collectionName) .insertOne(filteredObject); logger_manager_1.Log.dev(`Item added to ${collectionName}`, { id: result.insertedId.toString(), }); return result.insertedId.toString(); } catch (error) { return (0, data_manager_helpers_1.handleDbError)(`Failed to add item to ${collectionName}`, error); } }; /** * Updates an item by ID in a specified collection and returns the updated item. * @param collectionName - The name of the collection. * @param id - The ID of the item to update. * @param updateObject - The fields to update in the item. * @returns A Promise resolving with the updated item object if the update succeeded, otherwise `null`. */ const updateItemInCollection = async (collectionName, id, updateObject) => { ensureInitialized(); const objectId = (0, mongo_helpers_1.toObjectId)(id); if (!objectId) return null; try { const { matchedCount, modifiedCount } = await _db .collection(collectionName) .updateOne({ _id: objectId }, { $set: updateObject }); if (matchedCount === 1 && modifiedCount === 1) { const updatedItem = await _db .collection(collectionName) .findOne({ _id: objectId }); return transformId(updatedItem); } else { logger_manager_1.Log.warn(`Update failed for item with id ${id} in ${collectionName}`); return null; } } catch (error) { return (0, data_manager_helpers_1.handleDbError)(`Error updating item with id ${id} in ${collectionName}`, error); } }; /** * Finds an item by ID in a specified collection. * @param collectionName - The name of the collection. * @param id - The ID of the item to find. * @returns A `Promise` resolving with the item if found, or `null` if not found. */ const findItemByIdInCollection = async (collectionName, id) => { ensureInitialized(); const objectId = (0, mongo_helpers_1.toObjectId)(id); if (!objectId) return null; try { const foundDoc = await _db .collection(collectionName) .findOne({ _id: objectId }); return transformId(foundDoc); } catch (error) { return (0, data_manager_helpers_1.handleDbError)(`Error finding item with id ${id} in ${collectionName}`, error); } }; /** * Finds items by property-value pair in a specified collection. * @param collectionName - The name of the collection. * @param criteria - A collection of property-value pairs to match. All property-value pairs must be matched. An empty object will return all items. * @returns A `Promise` resolving with a item list if found, or an empty list if not found. */ const findItemsInCollection = async (collectionName, criteria) => { ensureInitialized(); if (!criteria || !collectionName) return null; try { const foundDocList = await _db .collection(collectionName) .find(criteria); const docList = await foundDocList.toArray(); const transformedList = docList .map(transformId) .filter((doc) => doc !== null); return transformedList; } catch (error) { return (0, data_manager_helpers_1.handleDbError)(`Error finding item with criteria ${criteria} in ${collectionName}`, error); } }; /** * Retrieves all items from a specified collection. * @param collectionName - The name of the collection. * @returns A Promise resolving with an array of items in the collection. */ const getAllInCollection = async (collectionName) => { ensureInitialized(); try { const results = (await _db.collection(collectionName).find({}).toArray()).map(transformId); return results.filter((doc) => doc !== null); } catch (error) { return (0, data_manager_helpers_1.handleDbError)(`Failed to retrieve items from ${collectionName}`, error); } }; /** * Removes an item by ID from a specified collection. * @param collectionName - The name of the collection. * @param id - The ID of the item to remove. * @returns A `Promise` resolving with `true` if the item was removed, otherwise `false`. */ const removeItemFromCollection = async (collectionName, id) => { ensureInitialized(); const objectId = (0, mongo_helpers_1.toObjectId)(id); if (!objectId) return false; try { const { acknowledged } = await _db .collection(collectionName) .deleteOne({ _id: objectId }); return acknowledged; } catch (error) { return (0, data_manager_helpers_1.handleDbError)(`Error removing item with id ${id} from ${collectionName}`, error); } }; /** * Clears all items in a specified collection. * @param collectionName - The name of the collection. * @returns A `Promise` resolving when the collection is cleared. * @throws Will throw an error if the operation fails. */ const clearCollection = async (collectionName) => { ensureInitialized(); try { await _db.collection(collectionName).drop(); } catch (error) { (0, data_manager_helpers_1.handleDbError)(`Failed to clear collection: ${collectionName}`, error); } }; /** * Checks if a specified collection is empty. * @param collectionName - The name of the collection. * @returns A `Promise` resolving with `true` if the collection is empty, otherwise `false`. */ const isCollectionEmpty = async (collectionName) => { ensureInitialized(); try { const count = await _db .collection(collectionName) .countDocuments({}); return count === 0; } catch (error) { return (0, data_manager_helpers_1.handleDbError)(`Failed to check if collection is empty: ${collectionName}`, error); } }; exports.MongoDBManager = { init, close, transformId, ensureInitialized, getCollectionChangeReadable, findItemByIdInCollection, findItemsInCollection, addItemToCollection, updateItemInCollection, getAllInCollection, removeItemFromCollection, clearCollection, isCollectionEmpty, }; /** * TestingMongoDBManager provides additional utilities for testing. * * @namespace TestingMongoDBManager * @private */ exports.TestingMongoDBManager = { ...exports.MongoDBManager, _setDatabaseInstance, _setClient, _setIsConnected, }; //# sourceMappingURL=mongo-data-manager.js.map