UNPKG

@magic.batua/account

Version:

The Account modules powers the user account management features of the Magic Batua platform.

270 lines 10.4 kB
"use strict"; /** * @module Database * @overview Defines the database manipulation functions used by the `Registry` module. * * @author Animesh Mishra <hello@animesh.ltd> * @copyright © 2018 Animesh Ltd. All Rights Reserved. */ Object.defineProperty(exports, "__esModule", { value: true }); const Chai = require("chai"); const Mongo = require("mongodb"); const Account_1 = require("./Account"); const Source_1 = require("./Source"); const error_1 = require("@magic.batua/error"); const expect = Chai.expect; const AccountCollection = "Users"; /** * @exports Database * The `Database` module powers the database read/write capabilities of the Magic Batua * platform. */ exports.description = "Powers the database read/write capabilities of the Magic Batua platform."; /** * Looks up the `phone` number in the user account registry. * * @param {string} phone Phone number to be searched * @param {Mongo.Db} db Database to be scoured * * @returns {Promise<Account | null>} An `Account` object if a match is found. If a * match is found but the referrer account has been soft-deleted, returns `null`. If * no match is found, returns `null`. * * @throws {ExternalError} If the search operation fails, a `424 Failed Dependency` * error is thrown. */ async function Find(phone, db) { try { let filter = { phone: phone }; let document = await db.collection(AccountCollection).findOne(filter); // If no document is found, return null if (document == null) { return null; } else if (document.isDeleted && document.recoverBy <= Date.now()) { return null; } else { return new Account_1.Account(document); } } catch (exception) { throw new error_1.ExternalError("Database search crashed unexpectedly.", "Azure CosmosDB", exception.toString()); } } exports.Find = Find; /** * Finds a user account registry entry that matches the given account information and * replaces that document with the `account` document provided. * * @param {Account} account Phone number to be searched * @param {Mongo.Db} db Database to be scoured * * @returns {Promise<Account>} The updated `Account` object if a match is found and * successfully replaced by the document provided. * * @throws {ExternalError} If the operation fails, a `424 Failed Dependency` error is thrown. */ async function FindAndReplace(account, db) { try { let filter = { _id: account._id }; let response = await db.collection(AccountCollection).findOneAndReplace(filter, account); // If update was successful, return the updated Account instance if (response.ok) { return new Account_1.Account(response.value); } else { throw response.lastErrorObject; } } catch (exception) { throw new error_1.ExternalError("Database search crashed unexpectedly.", "Azure CosmosDB", exception.toString()); } } exports.FindAndReplace = FindAndReplace; /** * Given a user `id`, retrieves the full account information stored against that `id` in * the database. * * @param {string} id Magic Batua user ID * @param {Mongo.Db} db Database to be searched * * @returns {Promise<Account | null>} An `Account` object if a match is found. If a * match is found but the referrer account has been soft-deleted, returns `null`. If * no match is found, returns `null`. * * @throws {ExternalError} If the search operation fails, a `424 Failed Dependency` * error is thrown. */ async function GetAccountByID(id, db) { try { let filter = { _id: new Mongo.ObjectId(id) }; let document = await db.collection(AccountCollection).findOne(filter); // If no document is found, return null if (document == null) { return null; } else if (document.isDeleted && document.recoverBy <= Date.now()) { return null; } else { return new Account_1.Account(document); } } catch (exception) { throw new error_1.ExternalError("Database search crashed unexpectedly.", "Azure CosmosDB", exception.toString()); } } exports.GetAccountByID = GetAccountByID; /** * When a user is referred to Magic Batua by their friends and family, their signup * request contains an `inviteCode` that helps us identify the referrer in our systems. * This is the method that does that heavy lifting. * * This method searches the database to find the `Account` having the same `referralCode` * property as the given `inviteCode`. If a match is found, it is deemed the rightful * referrer and returned to the calling scope. * * @param {string} inviteCode Referral code used at signup * @param {Mongo.Db} db Database to be scoured * * @returns {Promise<Account | null>} An `Account` object if a referrer is found. If * a match is found but the referrer account has been soft-deleted, returns `null`. If * no match is found, returns `null`. * * @throws {ExternalError} If the search operation fails, a `424 Failed Dependency` * error is thrown. */ async function GetReferrer(inviteCode, db) { try { let filter = { referralCode: inviteCode }; let result = await db.collection(AccountCollection).findOne(filter); // If no referrer is found, return null if (result == null) { return null; } else if (result.isDeleted && result.recoverBy <= Date.now()) { return null; } else { return new Account_1.Account(result); } } catch (exception) { throw new error_1.ExternalError("Database search crashed unexpectedly.", "Azure CosmosDB", exception.toString()); } } exports.GetReferrer = GetReferrer; /** * Inserts the given `account` document into the MongoDB database instance * held by `db`. No validation checks are run before writing the object to * database. * * @param {Account} account `Account` object to be written to database * @param {Mongo.Db} db The recipient MongoDB database * * @returns {Promise<Account>} A fully instantiated `Account` object * @throws {ExternalError} If the write operation fails, a `424 Failed Dependency` * error is thrown. */ async function Insert(account, db) { try { let response = await db.collection(AccountCollection).insertOne(account); // Check whether the operation executed correctly expect(response.result.ok).to.be.equal(1, "Account: Database.Insert() returned a non-ok result."); // Return the account object return new Account_1.Account(response.ops.pop(), Source_1.Source.Database); } catch (exception) { throw new error_1.ExternalError("Couldn't write object to database.", "Azure CosmosDB", exception.toString()); } } exports.Insert = Insert; /** * Checks whether the given `account` information is already registered with us. * * @param {Account} account Account information * @param {Mongo.Db} db Database to be scoured * * @returns {Promise<boolean>} `true` if a matching account is found. If a match is found * but the account has been soft-deleted, returns `false`. If no match is found, * returns `false`. * * @throws {ExternalError} If the search operation fails, a `424 Failed Dependency` * error is thrown. */ async function IsDuplicate(account, db) { try { let filter = { phone: account.phone }; let fields = { fields: { isDeleted: true, recoverBy: true } }; let result = await db.collection(AccountCollection).findOne(filter, fields); // If no matching document is found, return false if (result == null) { return false; } else if (result.isDeleted && result.recoverBy <= Date.now()) { return false; } else { return true; } } catch (exception) { throw new error_1.ExternalError("Database search crashed unexpectedly.", "Azure CosmosDB", exception.toString()); } } exports.IsDuplicate = IsDuplicate; /** * Updates the database object corresponding to the given `_id` in place. Doesn't run data * validation checks, so use very carefully and sparsely. Most of the update operations in the * software are done through the `Database.FindAndReplace()` method. Use that. * * **Only allows updates for `name`, `email` or `phone`.** * * If the `query` parameter contains a Magic Points transaction entry, then the entry is * pushed to the `pointsLedger.transactions` array. * * @param {string} id Magic Batua user `_id` * @param {any} query A JSON containing key-value pairs that should be updated. * @param {Mongo.Db} db MongoDB database instance * * @throws {ExternalError} If the operation fails, a `424 Failed Dependency` error is thrown. */ async function UpdateInPlace(id, query, db) { try { let filter = { _id: id }; let updates; let operations; if (query.name) { updates.name = query.name; } if (query.email) { updates.email = query.email; } if (query.phone) { updates.phone = query.phone; } // If a points transaction is supplied in the query, then push it to the transaction array if (query.pointsTransaction) { operations = { $set: updates, $push: { "pointsLedger.transactions": query.pointsTransaction } }; } else { operations = { $set: updates }; } let response = await db.collection(AccountCollection).updateOne(filter, operations); expect(response.result.ok).to.equal("1", "Database update returned a non-true result."); } catch (exception) { throw new error_1.ExternalError("Database update crashed unexpectedly.", "Azure CosmosDB", exception.toString()); } } exports.UpdateInPlace = UpdateInPlace; //# sourceMappingURL=Database.js.map