@just-in/core
Version:
A TypeScript-first framework for building adaptive digital health interventions.
368 lines • 13.7 kB
JavaScript
;
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) {
if (modifiedCount == 0) {
logger_manager_1.Log.warn(`No changes made for item with id ${id} in ${collectionName}`);
}
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