import { Db, MongoClient } from "mongodb"; import { assertIsDefined, assertNotEmpty } from "../../../util/assert"; import { Cosmos } from "../../Cosmos"; import { CosmosDatabase } from "../../CosmosDatabase"; import { MongoDatabaseImpl } from "./MongoDatabaseImpl"; import randomstring from "randomstring"; /** * class that represent a cosmos account * * Usage: * const cosmos = new Cosmos("AccountEndpoint=https://xxx.documents.azure.com:443/;AccountKey=xxx==;") * const db = cosmos.getDatabase("Database1") * * //Then use db to do CRUD / query * db.upsert("Users", user) * */ export class MongoImpl implements Cosmos { /** * native client instance to be hold in memory */ private readonly client: MongoClient; /** * A cache of , in order to get the instance quickly */ private readonly databaseMap: Map = new Map(); /** * Whether to auto generate _expireAtEnabled json field is ttl field is present. Used for compatibility for CosmosDB * */ private readonly expireAtEnabled; /** * Whether to auto generate _etag json field when created/updated. Used for compatibility for CosmosDB */ private readonly etagEnabled; /** * A flag in memory to represent whether the native mongo client is connected to mongodb */ private connected = false; constructor( connectionString: string | undefined, expireAtEnabled = false, etagEnabled = false, ) { assertIsDefined(connectionString, "connectionString"); assertNotEmpty(connectionString, "connectionString"); this.client = new MongoClient(connectionString); this.expireAtEnabled = expireAtEnabled; this.etagEnabled = etagEnabled; } public async getDatabase(db: string): Promise { const { client, databaseMap } = this; const database = databaseMap.get(db); if (database) { console.info("database exist."); return database; } console.info(`database not exist. create and connect. this.connected: ${this.connected}`); if (!this.connected) { await this.client.connect(); this.connected = true; console.info("mongo client connected"); } await this._createDatabaseIfNotExist(db); const newDatabase = new MongoDatabaseImpl(client, this); const ret = (newDatabase as unknown) as CosmosDatabase; databaseMap.set(db, ret); return ret; } public async deleteDatabase(db: string): Promise { this.databaseMap.delete(db); await this.client.db(db).dropDatabase(); } public async _createDatabaseIfNotExist(dbName: string): Promise { // get db list const databases = await this.client.db().admin().listDatabases(); const dbExists = databases.databases.some((db: { name: string }) => db.name === dbName); if (dbExists) { return this.client.db(dbName); } // if not exist, create the db by creating a collection console.log(`Database "${dbName}" does not exist. Creating...`); const db = this.client.db(dbName); const collectionName = "PING" + randomstring.generate(7); await db.createCollection(collectionName); console.info(`Database "${dbName}" created.`); await db.dropCollection(collectionName); return db; } public async close(): Promise { if (this.connected) { await this.client.close(); } } /** * Get expireAtEnabled * @returns expireAtEnabled in boolean */ public getExpireAtEnabled(): boolean { return this.expireAtEnabled; } /** * Get etagEnabled * @returns etagEnabled in boolean */ public getEtagEnabled(): boolean { return this.etagEnabled; } }