@magic.batua/account
Version:
The Account modules powers the user account management features of the Magic Batua platform.
270 lines • 10.4 kB
JavaScript
;
/**
* @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