const ValidateSchema = require('../../validation/validateSchema');
const CRUD = require('../../services/database/crud');
const { isObjectID } = require('../../helpers/database/relationalFields');
const { increaseDocProp } = require('../../helpers/database/dbHelpers');
/**
* Represents a global map in the application, extending the ValidateSchema class.
* @module GlobalMap
* @extends ValidateSchema
* @namespace Models
*/
class GlobalMap extends ValidateSchema {
/**
* Creates a new instance of the GlobalMap class.
* @param {Object} setup - The setup object.
* @param {Object} parent - The parent object.
* @throws {Error} If the creation of GlobalMap fails.
*/
constructor(setup, parent) {
const { _id, index, author, cod, createdAt, modifiedAt, collectionName} = setup || {};
super(setup.validationRules || {});
if (isObjectID(setup)) return;
const User = require('@models/collections/User');
try {
this.collectionName = collectionName;
this._id = _id && _id.toString();
this.index = index;
this.author = author || User.currentUser();
this.cod = cod;
this.createdAt = createdAt && new Date(createdAt).toLocaleString();
this.modifiedAt = modifiedAt && new Date(modifiedAt).toLocaleString();
this.getParent = () => parent;
} catch (err) {
throw new Error.Log(err);
}
}
/**
* Returns the parent object of the GlobalMap instance.
* @returns {Object} - The parent object.
*/
get parent() {
return this.getParent();
}
/**
* Returns the string representation of the index property.
* @returns {string} - The string representation of the index property.
*/
get stringIndex() {
return String(this.index);
}
/**
* Asynchronously retrieves the current user associated with the GlobalMap instance.
* @returns {Promise<Object>} - A promise resolving to the current user object.
* @throws {Error} If there is an error during the retrieval process.
*/
async getCurrentUser() {
const User = require('@models/collections/User');
const UID = User.currentUser();
const user = await CRUD.getDoc({
collectionName: 'users',
filter: UID
}).initialize();
return user;
}
/**
* Asynchronously saves the GlobalMap instance to the database.
* @param {string} collectionName - The name of the collection to save the instance.
* @returns {Promise<GlobalMap>} - A promise resolving to the saved GlobalMap instance.
* @throws {Error} If there is an error during the saving process.
*/
async saveDB(collectionName) {
const CRUDDB = require('@CRUD');
try {
const created = await CRUDDB.create(collectionName || this.collectionName, {...this});
if (created instanceof Error.Log) {
return new Error.Log(created);
}
if (created) {
Object.entries(created.initialize() || {}).map(([key, value]) => {
this[key] = value;
});
return this;
}
throw new Error.Log(created);
} catch (err) {
throw new Error.Log(err);
}
}
/**
* Asynchronously loads the GlobalMap instance from the database.
* @param {string} collectionName - The name of the collection to load the instance.
* @returns {Promise<GlobalMap>} - A promise resolving to the loaded GlobalMap instance.
* @throws {Error} If there is an error during the loading process.
*/
async loadDB(collectionName) {
if (!collectionName) {
collectionName = this.collectionName;
}
try {
const loaded = await CRUD.getDoc({collectionName, filter: this._id}).defaultPopulate();
if (loaded instanceof Error.Log) {
throw new Error.Log(loaded);
}
return loaded.initialize();
} catch (err) {
throw new Error.Log(err);
}
}
/**
* Asynchronously updates the GlobalMap instance in the database.
* @param {Object} updateParams - The update parameters including collectionName, filter, and data.
* @returns {Promise<GlobalMap>} - A promise resolving to the updated GlobalMap instance.
* @throws {Error} If there is an error during the update process.
*/
async updateDB({collectionName, filter, data}) {
const crud = require('../../services/database/crud');
const collection = collectionName || this.collectionName;
try {
if (!collection) throw new Error.Log('database.missing_params', 'collectionName', '_Global.updateDB');
const loaded = await crud.update({collectionName: collection, filter: filter || this._id, data, options: {returnDocs: true} });
if (loaded instanceof Error.Log) {
throw new Error.Log(loaded);
}
return loaded.initialize();
} catch (err) {
throw new Error.Log(err);
}
}
/**
* Asynchronously deletes the GlobalMap instance from the database.
* @param {string} collectionName - The name of the collection to delete the instance.
* @param {Object} filter - The filter object for deletion.
* @returns {Promise<Object>} - A promise resolving to the deletion result.
* @throws {Error} If there is an error during the deletion process.
*/
async deleteDB(collectionName, filter) {
try {
const deleted = await CRUD.del({
collectionName: collectionName || this.collectionName,
filter: filter || this._id
});
if (deleted instanceof Error.Log) {
throw deleted;
}
return deleted;
} catch (err) {
throw new Error.Log(err);
}
}
/**
* Asynchronously increases a property value in the GlobalMap instance.
* @param {string} propKey - The property key to increase.
* @param {number} value - The value by which the property should be increased (default is 1).
* @returns {Promise<GlobalMap>} - A promise resolving to the updated GlobalMap instance.
* @throws {Error} If there is an error during the property increase process.
*/
async increaseProp(propKey, value) {
if (!propKey) throw new Error.Log()
const increaseAmount = value || 1;
const increaseValue = {[propKey]: increaseAmount};
try {
if (!this.collectionName || !propKey) {
return new Error.Log('database.missing_params', ['this.collectionName', 'propKey'], '_Global.updateDB')
}
const increased = await increaseDocProp(this.collectionName, {_id: this._id}, increaseValue);
if (increased instanceof Error.Log) {
return increased;
}
increased[propKey] = increased[propKey] + increaseAmount;
return increased;
} catch (err) {
throw new Error.Log('helpers.increase_doc_prop', this.collectionName, this._id, increaseValue);
}
}
/**
* Encrypts and sets the specified field with the provided value. If the field does not have a current encrypted value,
* it creates a new SafeValue instance and updates the field in the database. If the field already has an encrypted value,
* it updates the existing SafeValue instance with the new value.
*
* @param {string} fieldName - The name of the field to be encrypted and updated.
* @param {string} value - The raw value to be encrypted and set.
* @returns {Promise<Object>} A Promise that resolves to an object containing success status, message, and data.
* @throws {Error.Log} If there is an error during encryption, database update, or if a required value is missing.
* @async
*/
async setEncryptField(fieldName, value) {
const SafeValue = require('4hands-api/src/models/collections/SafeValue');
const currValue = this[fieldName];
if (!value) {
throw new Error.Log({ name: `It's required to have a value to proceed on setting a safe value!`});
}
if (!currValue || currValue.isEmpty) {
const newSafeValue = await SafeValue.createEncrypt(value);
const updated = await this.updateDB({
data: { [fieldName]: newSafeValue.id }
});
if (!updated || updated instanceof Error.Log) {
throw new Error.Log(updated);
} else {
return { success: true, message: 'New safe value created!', data: newSafeValue };
}
} else {
const updated = await currValue.setEncrypted(value);
if (!updated || updated instanceof Error.Log) {
throw new Error.Log(updated);
} else {
return { success: true, message: 'SafeValue updated!', data: updated };
}
}
}
}
module.exports = GlobalMap;