UNPKG

yekonga-server

Version:
1,163 lines (1,003 loc) 90.4 kB
/*global Yekonga, serverLibrary */ // @ts-nocheck const H = Yekonga.Helper; const fs = serverLibrary.fs; const moment = serverLibrary.moment; const excel = serverLibrary.excel; const path = serverLibrary.path; const PDF = serverLibrary.PDF; const ObjectId = serverLibrary.mongodb.ObjectId; const AuditTrail = require('./auditTrail'); function beforeAndAfterCallback(action, name, params, isAdmin) { const { accessRole, route } = params || {} return Yekonga.Cloud.getBeforeAndAfterCallback(action, name, accessRole, route, isAdmin); } module.exports = class ModelControl { constructor(model) { this.collection = null; this.modelTitle = ''; this.modelClass = null; this.modelClassVariable = null; this.modelFields = {}; this.modelRelationFields = []; this.databaseType = null; this.auditTrail = null; this.ModelController = {}; this.hasProfileId = false; this.fileFields = []; this.timestampFields = []; this.keyFields = []; this.init(model); this.ModelController.ignoreAction = false; this.ModelController.findOne = this.buildFindOne(this); this.ModelController.find = this.buildFind(this); this.ModelController.paginate = this.buildPaginate(this); this.ModelController.download = this.buildDownload(this); this.ModelController.summary = this.buildSummary(this); this.ModelController.count = this.buildCount(this); this.ModelController.sum = this.buildSum(this); this.ModelController.max = this.buildMax(this); this.ModelController.min = this.buildMin(this); this.ModelController.graph = this.buildGraph(this); this.ModelController.create = this.buildCreate(this); this.ModelController.update = this.buildUpdate(this); this.ModelController.import = this.buildImport(this); this.ModelController.delete = this.buildDelete(this); this.ModelController.force = this.buildForce(this); const conf = Yekonga.Config.database[Yekonga.Config.defaultDatabase]; this.databaseType = (conf && conf.type) ? conf.type : 'mongodb'; } init(model) { var updatedCollection = this.collection; for (const key in model) { if (key == "profileId") { this.hasProfileId = true; break; } } if (model._id) { this.collection = model._id.collection; updatedCollection = this.collection.startsWith('_') ? this.collection.substr(1) : this.collection; this.modelTitle = H.getTitle(H.toSingular(updatedCollection)); this.modelClass = H.getClass(updatedCollection); this.modelClassVariable = H.getClassVariable(updatedCollection, true); } if (!model._id.secondaryKey) { model._id.secondaryKey = H.getVariable(`${H.toSingular(H.getUnderscore(updatedCollection))}_id`); } this.secondaryKey = model._id.secondaryKey; this.modelFields = model; this.auditTrail = new AuditTrail({ collection: this.collection, modelClass: this.modelClass, secondaryKey: this.secondaryKey, }); for (const key in this.modelFields) { if (H.isTimestampColumn(key, this.modelFields[key])) { this.timestampFields.push(key); } else if(H.isColumnUrl(key, this.modelFields[key])) { this.fileFields.push(key); } if(H.isObjectID(key, this.modelFields[key])) { this.keyFields.push(key); } } this.setRelations(); } create() { return this.ModelController; } setRelations() { let parents = {}; let children = {}; let parentsArray = H.getParents(this.modelFields); let childrenArray = H.getChildren(this.modelFields); this.modelRelationFields.push(this.secondaryKey); for (const item of parentsArray) { this.modelRelationFields.push(item._id.foreignKey); // parents[H.getClass(item._id.collection)] = H.copyJson(item._id); parents[item._id.relatedName] = H.copyJson(item._id); } for (const item of childrenArray) { this.modelRelationFields.push(item._id.foreignKey); // children[H.getClass(item._id.collection)] = H.copyJson(item._id); children[item._id.relatedName] = H.copyJson(item._id); } this.parents = parents; this.children = children; } async getWhere(self, key, operation, value, context) { var result = {}; if (key == 'AND') { var newValue = []; for (const whr of value) { newValue.push((await self.processWhere(self, whr, context))) } result = { key: '$and', value: newValue }; } else if (key == 'OR') { var newValue = []; for (const whr of value) { newValue.push((await self.processWhere(self, whr, context))) } result = { key: '$or', value: newValue }; } else if (key == 'NOR') { var newValue = []; for (const whr of value) { newValue.push((await self.processWhere(self, whr, context))) } result = { key: '$nor', value: newValue }; } else { result = await self.getWhereItem(self, key, value, operation); } return result; } getWhereItem(self, key, value, operation) { var result = null; if( Array.isArray(self.keyFields) && self.keyFields.includes(key) ) { if( typeof value == "string" && value.length == 24 ) { value = new ObjectId(value); } else if(Array.isArray(value)) { for (let i = 0; i < value.length; i++) { if( typeof value[i] == "string" && value[i].length == 24 ) { value[i] = new ObjectId(value[i]); } } } } switch (operation) { case 'equalTo': result = { key, value: { '$eq': value } }; break; case 'notEqualTo': result = { key, value: { '$ne': value } }; break; case 'lessThan': value = H.convertCalculatedValue(value); result = { key, value: { '$lt': value } }; break; case 'notLessThan': value = H.convertCalculatedValue(value); result = { key, value: { '$not': { '$lt': value } } }; break; case 'lessThanOrEqualTo': value = H.convertCalculatedValue(value); result = { key, value: { '$lte': value } }; break; case 'notLessThanOrEqualTo': value = H.convertCalculatedValue(value); result = { key, value: { '$not': { '$lte': value } } }; break; case 'greaterThan': value = H.convertCalculatedValue(value); result = { key, value: { '$gt': value } }; break; case 'notGreaterThan': value = H.convertCalculatedValue(value); result = { key, value: { '$not': { '$gt': value } } }; break; case 'greaterThanOrEqualTo': value = H.convertCalculatedValue(value); result = { key, value: { '$gte': value } }; break; case 'notGreaterThanOrEqualTo': value = H.convertCalculatedValue(value); result = { key, value: { '$not': { '$gte': value } } }; break; case 'in': result = { key, value: { '$in': value } }; break; case 'all': result = { key, value: { '$all': value } }; break; case 'notIn': result = { key, value: { '$nin': value } }; break; case 'exists': var exists = (value ? true : false); if(exists) { result = { key, value: { '$exists': true, '$nin': [null,undefined] } }; } else { var valueA = {}; var valueB = {}; valueA[key] = { '$exists': false }; valueB[key] = { '$in': [null,undefined] }; result = { key: '$or', value: [valueA, valueB] }; } break; case 'matchesRegex': result = { key, value: (new RegExp(value, "i")) }; break; case 'options': result = { key, value: { '$eq': value } }; break; case 'text': result = { key, value: { '$eq': value } }; break; case 'inQueryKey': result = { key, value: { '$eq': value } }; break; case 'notInQueryKey': result = { key, value: { '$eq': value } }; break; default: result = { key, value }; break; } return result; } async processWhere(self, where, context) { var filter = {} const { req } = context; for (const key in where) { const valueObj = where[key]; const whereName = H.getClass(key); if (['AND', 'OR', 'NOR'].includes(key)) { const params = await self.getWhere(self, key, key, valueObj, context); filter[params.key] = params.value; } else if (self.modelFields[key] && self.databaseType == 'mongodb' && (self.modelRelationFields.includes(key) || self.secondaryKey === key)) { let _value = valueObj; if (!(valueObj instanceof ObjectId) && typeof valueObj == 'string' && valueObj.length === 24) { // @ts-ignore _value = new ObjectId(valueObj); const params = await self.getWhere(self, key, 'equalTo', _value, context); filter[H.getColumn(params.key)] = params.value; } else if (Array.isArray(valueObj)) { _value = valueObj.map((v) => { // @ts-ignore return (typeof v == 'string' && v.length === 24) ? new ObjectId(v) : v; }); const params = await self.getWhere(self, key, 'in', _value, context); filter[H.getColumn(params.key)] = params.value; } else if (valueObj && !(valueObj instanceof ObjectId) && typeof valueObj == 'object') { for (const operation in valueObj) { let _value = valueObj[operation]; if (!(_value instanceof ObjectId) && typeof _value == 'string' && _value.length === 24) { // @ts-ignore _value = new ObjectId(_value); } else if (Array.isArray(_value)) { _value = _value.map((v) => { // @ts-ignore return (typeof v == 'string' && v.length == 24) ? new ObjectId(v) : v; }); } const params = await self.getWhere(self, key, operation, _value, context); if (filter[H.getColumn(params.key)]) { if (params.value && typeof params.value == 'object' && !(params.value instanceof Date) ) { for (const k in params.value) { filter[H.getColumn(params.key)][k] = params.value[k]; } } else { filter[H.getColumn(params.key)] = params.value; } } else { filter[H.getColumn(params.key)] = params.value; } } } else { const params = await self.getWhere(self, key, 'equalTo', _value, context); filter[H.getColumn(params.key)] = params.value; } } else if (self.modelFields[key] && valueObj && typeof valueObj == 'object' && !(valueObj instanceof ObjectId)) { for (const operation in valueObj) { const value = valueObj[operation]; const params = await self.getWhere(self, key, operation, value, context); if (filter[H.getColumn(params.key)]) { if (params.value && typeof params.value == 'object' && !(params.value instanceof Date)) { if (typeof filter[H.getColumn(params.key)] !== 'object') { filter[H.getColumn(params.key)] = {}; } for (const k in params.value) { filter[H.getColumn(params.key)][k] = params.value[k]; } } else { filter[H.getColumn(params.key)] = params.value; } } else { filter[H.getColumn(params.key)] = params.value; } } } else if (self.modelFields[key]) { let _value = valueObj; if (key === self.secondaryKey) { if (typeof valueObj == 'string' && valueObj.length === 24) { // @ts-ignore _value = new ObjectId(valueObj); } else if (Array.isArray(valueObj)) { _value = valueObj.map((v) => { // @ts-ignore return (typeof v == 'string' && v.length === 24) ? new ObjectId(v) : v; }); } } const params = await self.getWhere(self, key, 'equalTo', _value, context); filter[H.getColumn(params.key)] = params.value; } else if (!self.modelFields[key] && key.startsWith('_') && self.modelFields[key.substring(1)]) { let _value = valueObj; if (key === self.secondaryKey) { if (typeof valueObj == 'string' && valueObj.length === 24) { // @ts-ignore _value = new ObjectId(valueObj); } else if (Array.isArray(valueObj)) { _value = valueObj.map((v) => { // @ts-ignore return (typeof v == 'string' && v.length === 24) ? new ObjectId(v) : v; }); } } if (_value && typeof _value === 'object') { filter[key] = _value; } else { const params = await self.getWhere(self, key, 'equalTo', _value, context); filter[params.key] = params.value; } } else if (!self.modelFields[key]) { if ( self.parents[key] || self.children[key] ) { let ids = []; let objectToGetKey = {}; objectToGetKey[key] = valueObj; let objectValueId = H.getObjectUUID(objectToGetKey); // console.log(objectToGetKey); // console.log(objectValueId); // console.log(context.params) // console.log(context.filter) // console.log('============================') if (req && !req.Yekonga.Temporary) req.Yekonga.Temporary = {}; if (req && req.Yekonga.Temporary[objectValueId]) { ids = req.Yekonga.Temporary[objectValueId]; } else { let localSecondaryKey = null; let localModelClass = null; if (self.parents[key]) { localSecondaryKey = self.parents[key].secondaryKey; localModelClass = H.getClass(self.parents[key].collection); } else if (self.children[key]) { localSecondaryKey = self.children[key].foreignKey; localModelClass = H.getClass(self.children[key].collection); } if (Yekonga.Model[localModelClass]) { let sourceIds = await Yekonga.Model[localModelClass].find(valueObj, { ...context, select: [localSecondaryKey] }, true); if (Array.isArray(sourceIds)) { for (let i = 0; i < sourceIds.length; i++) { var item = sourceIds[i]; var v = item[localSecondaryKey]; if (typeof v == 'string' && v.length === 24) { v = new ObjectId(v); } if (!(typeof v === 'undefined' || v === null)) { if (!ids.includes(v)) ids.push(v); } } } } } if (Array.isArray(ids)) { let params = null; if (self.parents[key]) { params = await self.getWhere(self, self.parents[key].foreignKey, 'in', ids, context); } else if (self.children[key]) { params = await self.getWhere(self, self.children[key].foreignKey, 'in', ids, context); } if (params) { if(filter[H.getColumn(params.key)]) { for (const k in filter[H.getColumn(params.key)]) { if (Object.hasOwnProperty.call(filter[H.getColumn(params.key)], k)) { params.value[k] = filter[H.getColumn(params.key)][k]; } } } filter[H.getColumn(params.key)] = params.value; } } } } } return filter; } async model(self, context, isAdmin = false) { if(typeof context === 'boolean') { isAdmin = context; context = { }; } if (!context) context = {} var filterQuery = {}; // @ts-ignore var { filter, params } = context; const { beforeFind, afterFind, parent, Auth, Client, headers, req } = context; // @ts-ignore if (parent && !context.accessRole) { context.accessRole = (parent.params && parent.params.accessRole) ? parent.params.accessRole : null; } var newFilter = {}; if (!filter) filter = {}; if (self.hasProfileId && !context.accessRole) { // if (req && req.Yekonga.Auth && req.Yekonga.Auth.profileId) { // // filter["profileId"] = { equalTo: req.Yekonga.Auth.profileId } // } else if (!isAdmin) { // if (!filter["profileId"]) { // const profileId = (Auth && Auth.profileId) ? Auth.profileId : null; // if (profileId) { // // filter["profileId"] = { equalTo: profileId } // } // } // } } var InjectFunction = beforeAndAfterCallback('beforeFind', self.modelClass, params, isAdmin); if (InjectFunction) { var response = await InjectFunction({ filter, ...context, }); if (response === false) { return null; } else if (typeof response != 'undefined') { filter = response; } } for (const key in filter) { if (Object.prototype.hasOwnProperty.call(filter, key)) { const value = filter[key]; if (value === null || typeof value == 'string' || typeof value == 'number' || typeof value == 'bigint' || typeof value == 'boolean') { newFilter[key] = { '$eq': value }; } else { newFilter[key] = value; } } } var dataModel = Yekonga.DB.table(self.collection); if (newFilter) { filterQuery = await self.processWhere(self, newFilter, context); } filterQuery = await this.setTimestampFilters(self, dataModel, filterQuery); dataModel.where(filterQuery); if (params) { // console.log(params); if (params.page) { dataModel.page(params.page); } if (params.limit) { dataModel.limit(params.limit); } if (params.groupBy) { if (Array.isArray(params.groupBy)) { let value = {} for (const key of params.groupBy) { if (typeof key == 'string') { value[H.getColumn(key)] = `$${H.getColumn(key)}`; } else if (key && typeof key == 'object') { value = key; } } dataModel.groupBy("_id", value); } else { let value = {} if (typeof params.groupBy == 'string') { value[H.getColumn(params.groupBy)] = `$${H.getColumn(params.groupBy)}`; } else if (params.groupBy && typeof params.groupBy == 'object') { value = params.groupBy; } dataModel.groupBy("_id", value); } } if (params.orderBy) { if (Array.isArray(params.orderBy)) { for (const orderBy of params.orderBy) { if (orderBy.key) { var key = (orderBy.key == '_id' || orderBy.key.startsWith('_id.')) ? orderBy.key : H.getColumn(orderBy.key); dataModel.orderBy(key, orderBy.value); } } } else if (params.orderBy.key) { var key = (params.orderBy.key == '_id' || params.orderBy.key.startsWith('_id.')) ? params.orderBy.key : H.getColumn(params.orderBy.key); dataModel.orderBy(key, params.orderBy.value); } } if (params.addedFields) { dataModel.addField(params.addedFields); } } return dataModel; } async setTimestampFilters(self, model, filters) { var addedFilters = {}; var formattedFilters = {}; for (const key in filters) { if (self.timestampFields.includes(key)) { // console.log(self.timestampFields); var format = '%Y-%m-%d'; var value = filters[key]; if (value && typeof value == 'object' && !(value instanceof Date)) { for (const operator in value) { if (Object.hasOwnProperty.call(value, operator)) { const _value = value[operator]; if (_value && typeof _value == "string") { format = H.formatJsToMongo(H.getTimeFormat(_value)); formattedFilters[`_${key}`] = value; } else if (_value && typeof _value == "object" && !(_value instanceof Date)) { for (const _operator in _value) { if (Object.hasOwnProperty.call(_value, _operator)) { const __value = _value[_operator]; if (__value && typeof __value == "string") { format = H.formatJsToMongo(H.getTimeFormat(__value)); formattedFilters[`_${key}`] = _value; } } } } else { formattedFilters[`${key}`] = value; } } } } addedFilters[`_${key}`] = { $dateToString: { format: format, date: `$${key}` } } } else { formattedFilters[key] = filters[key]; } } // if (self.modelClass == "Event") { // console.log(filters); // } model.addField(addedFilters); return formattedFilters; } buildForce(self) { return async(force = true) => { self.ModelController.ignoreAction = force; return self.ModelController; } } buildFindOne(self) { return async(filter, context, isAdmin = false) => { authentication(self.modelClass, `${self.modelClassVariable}.information`, `view ${self.modelTitle} information`); if(typeof context === 'boolean') { isAdmin = context; context = { }; } if (!context) context = {}; context.filter = filter; const { select, params, parent, Auth, Client, headers, beforeFind, afterFind, } = context; var model = await self.model(self, context, isAdmin); if (model === null) { throw new Yekonga.Error.Forbidden(getRejectedByAction({modelClass: self.modelClass, params, action: 'before find'})) } if (select && Array.isArray(select) && select.length) { model.select(H.formatToColumn(select)); } var result = await model.findOne(); result = H.formatToVariables(result, self.secondaryKey, self.collection); var afterFindInjectFunction = beforeAndAfterCallback('afterFind', self.modelClass, params, isAdmin); if (afterFindInjectFunction) { await afterFindInjectFunction({ result, ...context, }) } if(result) { result.__ = { collection: self.collection, secondaryKey: self.secondaryKey, } } return result; } } buildFind(self) { return async(filter, context, isAdmin = false) => { authentication(self.modelClass, `${self.modelClassVariable}.list`, `see ${self.modelTitle} list`); if(typeof context === 'boolean') { isAdmin = context; context = { }; } if (!context) context = {}; context.filter = filter; const { select, params, parent, Auth, Client, headers, beforeFind, afterFind, } = context; var model = await self.model(self, context, isAdmin); if (model === null) throw new Yekonga.Error.Forbidden(getRejectedByAction({modelClass: self.modelClass, params, action: 'before find'})); if (select && Array.isArray(select) && select.length) { model.select(H.formatToColumn(select)) } var result = await model.find(); result = H.formatToVariables(result, self.secondaryKey, self.collection); if (!Array.isArray(result)) { result = []; } var afterFindInjectFunction = beforeAndAfterCallback('afterFind', self.modelClass, params, isAdmin); if (afterFindInjectFunction) { await afterFindInjectFunction({ result, ...context, }) } return result; } } buildPaginate(self) { return async(filter, context, isAdmin = false) => { authentication(self.modelClass, `${self.modelClassVariable}.list`, `see ${self.modelTitle} list`); if(typeof context === 'boolean') { isAdmin = context; context = { }; } if (!context) context = {}; context.filter = filter; const { select, params, parent, Auth, Client, headers, beforeFind, afterFind, } = context; var model = await self.model(self, context, isAdmin); if (model === null) throw new Yekonga.Error.Forbidden(getRejectedByAction({modelClass: self.modelClass, params, action: 'before find'})); if (select && Array.isArray(select) && select.length) { model.select(H.formatToColumn(select)) } var result = await model.paginate(); result.data = H.formatToVariables(result.data, self.secondaryKey, self.collection); var afterFindInjectFunction = beforeAndAfterCallback('afterFind', self.modelClass, params, isAdmin); if (afterFindInjectFunction) { await afterFindInjectFunction({ result, ...context, }); } return result; } } buildDownload(self) { var headingColumnNames = []; for (const key in this.modelFields) { headingColumnNames.push(H.getTitle(key)); } return async(filter, context, isAdmin = false) => { authentication(self.modelClass, `${self.modelClassVariable}.download`, `download ${self.modelTitle} list`, false); if(typeof context === 'boolean') { isAdmin = context; context = { }; } if (!context) context = {}; context.filter = filter; const { select, params, parent, Auth, Client, headers, beforeFind, afterFind, req, res} = context; var data = []; var queryBody = null; if( req && req.Yekonga && req.Yekonga.Client && req.Yekonga.Client.body && req.Yekonga.Client.body.variables && req.Yekonga.Client.body.variables.download ) { queryBody = req.Yekonga.Client.body.variables.download; } console.log('downloadQuery', 'Start'); if (queryBody) { data = await Yekonga.Helper.getGraphql(queryBody, Yekonga.Client.headers, 'list', context); } else { var model = await self.model(self, context, isAdmin); if (model === null) throw new Yekonga.Error.Forbidden(getRejectedByAction({modelClass: self.modelClass, params, action: 'before download'})); if (select && Array.isArray(select) && select.length) { model.select(H.formatToColumn(select)) } data = await model.limit(null).find(); } console.log('downloadQuery', 'Loaded'); if (Array.isArray(data)) { var timezone = (headers)? (headers['timezone'] || headers['Timezone']): null; console.log("download timezone:", timezone); data = data.map(e => { // console.log(e); return ModelControl.getDataValue(e, timezone); }); } else { // console.log() data = []; } // console.log('downloadQuery', 'sorted'); // console.log("downloadQuery", headingColumnNames) headingColumnNames = ModelControl.getHeading(headingColumnNames, data); // console.log("downloadQuery", headingColumnNames) var publicPath = Array.isArray(Yekonga.Config.public) ? Yekonga.Config.public[0] : Yekonga.Config.public; var time = H.toTimestampString(null, 'YYYY-MM-DD-HHmm'); // YYYY-MM-DD-HHmm var ext = ModelControl.getExtName(params.downloadType); var filename = `${H.getLink(self.modelClass)}-${time}.${ext}`; var dirpath = `${path.join(serverLibrary.__dirname, publicPath)}/tmp/`; var filepath = `${dirpath}/${filename}`; var fileUrl = `//${Yekonga.Config.domain}/download/${filename}`; if (!fs.existsSync(filepath)) { H.createFile(filepath, ''); } var result = { filename: null, url: null, type: params.downloadType, size: 0, } if (['PDF', 'PRINT', 'JPG', 'PNG', 'IMAGE'].includes(result.type)) { var html = this._dataHtmlContent(headingColumnNames, data); var orientation = (params.orientation) ? params.orientation : 'portrait'; var options = { format: 'A4', orientation: orientation, type: ext, quality: "100", }; options.margin = { top: '10px', right: '10px', bottom: '10px', left: '10px' }; // @ts-ignore await H.PDF(html, filepath, options); } else if (result.type == 'EXCEL') { const wb = new excel.Workbook(); const ws = wb.addWorksheet('Raw Data'); //Write Column Title in Excel file let headingColumnIndex = 1; headingColumnNames.forEach(heading => { ws.cell(1, headingColumnIndex++).string(H.getTitle(heading)); }); //Write Data in Excel file let rowIndex = 2; data.forEach(record => { let columnIndex = 1; for (const key of headingColumnNames) { var v = record[key]; if(Array.isArray(v)) v = v.join(" / "); ws.cell(rowIndex, columnIndex++).string(`${(typeof v != 'undefined' && v != null)? v: ''}`); } rowIndex++; }); wb.write(filepath); } else { const csvData = [headingColumnNames.map(e=>H.getTitle(e)).join(',')]; //Write Data in Excel file data.forEach(record => { let _data = []; for (const key of headingColumnNames) { var v = record[key]; if(Array.isArray(v)) v = v.join(" / "); if(typeof v == 'string'){ if(v.includes(',')) v = JSON.stringify(v); else if(v.includes("'")) v = JSON.stringify(v); } _data.push(v); } csvData.push(_data); }); fs.writeFileSync(filepath, csvData.join('\n')); } result.filename = filename; result.url = fileUrl; await H.wait(5000); var afterFindInjectFunction = beforeAndAfterCallback('afterFind', self.modelClass, params, isAdmin); if (afterFindInjectFunction) { await afterFindInjectFunction({ result, ...context, }) } return result; } } buildCreate(self) { var secondaryKey = this.secondaryKey; return async(input, context, isAdmin = false) => { authentication(self.modelClass, `${self.modelClassVariable}.create`, `${self.modelTitle}`); if(typeof context === 'boolean') { isAdmin = context; context = { }; } if (!context) context = {}; if (!context.params) context.params = {}; const { select, params, parent, Auth, Client, headers, beforeFind, afterFind, } = context; const orgInput = H.copyJson(context.params.input); const { beforeSave, afterSave, beforeCreate, afterCreate } = context; var result = null; try { if (Array.isArray(input)) { var _inputValid = []; for (let i = 0; i < input.length; i++) { const v = input[i]; if (v && !v[secondaryKey]) v[secondaryKey] = H.getHexString(); const value = H.formatToColumn(H.getValidFields({ input: v, validFields: self.modelFields, isCreate: true, relations: self.modelRelationFields, request: context.req, fileFields: self.fileFields, }).pop()) if (self.modelClass === 'User') { if (!value.email && !value.phone) { throw new Yekonga.Error.Forbidden(`User must have email or phone number`); } if (typeof value.email == 'string' && !H.isEmail(value.email)) { throw new Yekonga.Error.Forbidden(`Email can't be empty or must be valid`); } if (typeof value.phone == 'string' && !H.isPhone(value.phone)) { throw new Yekonga.Error.Forbidden(`Phone number can't be empty or must be valid`); } if (value.email && H.isEmailIdentifier()) await H.uniqueEmail(value.email, null); if (value.phone && H.isPhoneIdentifier()) await H.uniquePhone(value.phone, null); if (value.whatsapp && H.isWhatsappIdentifier()) await H.uniqueWhatsapp(value.whatsapp, null); if (value.username && H.isUsernameIdentifier()) await H.uniqueUsername(value.username, null); } if (self.hasProfileId && self.modelClass == 'Profile') { value.profileId = new ObjectId(H.getHexString()); } _inputValid.push(value); } input = _inputValid; } else { if (!input[secondaryKey]) input[secondaryKey] = H.getHexString(); // {input, validFields, isCreate = false, fileFields = [], relations = [], request = {}} input = H.formatToColumn(H.getValidFields({ input: input, validFields: self.modelFields, isCreate: true, relations: self.modelRelationFields, request: context.req, fileFields: self.fileFields, }).pop()); if (self.modelClass == 'User') { if (input.email && H.isEmailIdentifier()) await H.uniqueEmail(input.email, null); if (input.phone && H.isPhoneIdentifier()) await H.uniquePhone(input.phone, null); if (input.whatsapp && H.isWhatsappIdentifier()) await H.uniqueWhatsapp(input.whatsapp, null); if (input.username && H.isUsernameIdentifier()) await H.uniqueUsername(input.username, null); } if (self.hasProfileId && self.modelClass == 'Profile') { input.profileId = new ObjectId(H.getHexString()) } } var beforeSaveInjectFunction = beforeAndAfterCallback('beforeSave', self.modelClass, params, isAdmin); if (beforeSaveInjectFunction) { var _input = await beforeSaveInjectFunction({ input, result, ...context, }); if (_input === false) throw new Yekonga.Error.Forbidden(getRejectedByAction({modelClass: self.modelClass, params, action: 'before save'})); if (_input) input = _input; } var beforeCreateInjectFunction = beforeAndAfterCallback('beforeCreate', self.modelClass, params, isAdmin); if (beforeCreateInjectFunction) { var _input = await beforeCreateInjectFunction({ input, result, ...context, }); if (_input === false) throw new Yekonga.Error.Forbidden(getRejectedByAction({modelClass: self.modelClass, params, action: 'before create'})); if (_input) input = _input; } // {input, validFields, isCreate = false, fileFields = [], relations = [], request = {}} input = H.formatToColumn(H.getValidFields({ input: input, validFields: self.modelFields, isCreate: true, relations: self.modelRelationFields, request: null, fileFields: self.fileFields, secondaryKey: self.secondaryKey, })); self.auditTrail.setOldValues(context, isAdmin); result = await Yekonga.DB.table(self.collection, H.getColumn(`${secondaryKey}`)).create(input); self.auditTrail.record(orgInput, 'create', result.data); if (Array.isArray(result.data)) { result.data = result.data.map(v => { return H.formatToVariables(v, secondaryKey, self.collection); }); } else { result.data = H.formatToVariables(result.data, secondaryKey, self.collection); } } catch (error) { console.error(error); result = { error: error.message }; } var afterSaveInjectFunction = beforeAndAfterCallback('afterSave', self.modelClass, params, isAdmin); if (afterSaveInjectFunction) { await afterSaveInjectFunction({ result, input, ...context, }) } var afterCreateInjectFunction = beforeAndAfterCallback('afterCreate', self.modelClass, params, isAdmin); if (afterCreateInjectFunction) { await afterCreateInjectFunction({ result, input, ...context, }) } return result; } } buildUpdate(self) { return async(input, filter, context, isAdmin = false) => { authentication(self.modelClass, `${self.modelClassVariable}.edit`, `${self.modelTitle} data`); if(typeof context === 'boolean') { isAdmin = context; context = { }; } if (!context) context = {}; context.filter = filter; const { select, params, parent, Auth, Client, headers, beforeFind, afterFind, beforeSave, afterSave, beforeUpdate, afterUpdate } = context; var model = null; var result = null; try { // {input, validFields, isCreate = false, fileFields = [], relations = [], request = {}} input = H.getValidFields({ input: input, validFields: self.modelFields, isCreate: false, relations: self.modelRelationFields, request: context.req, fileFields: self.fileFields, secondaryKey: self.secondaryKey, }).pop(); input = H.formatToColumn(input); model = await self.model(self, context, isAdmin); var beforeSaveInjectFunction = beforeAndAfterCallback('beforeSave', self.modelClass, params, isAdmin); if (beforeSaveInjectFunction) { var _input = await beforeSaveInjectFunction({ input, filter, ...context, }); if (_input === false) throw new Yekonga.Error.Forbidden(getRejectedByAction({modelClass: self.modelClass, params, action: 'before save'})); if (_input) input = _input; } var beforeUpdateInjectFunction = beforeAndAfterCallback('beforeUpdate', self.modelClass, params, isAdmin); if (beforeUpdateInjectFunction) { var _input = await beforeUpdateInjectFunction({ input, filter, ...context, }); if (_input === false) throw new Yekonga.Error.Forbidden(getRejectedByAction({modelClass: self.modelClass, params, action: 'before update'})); if (_input) input = _input; } if (self.modelClass == 'User') { if (input.email && H.isEmailIdentifier()) await H.uniqueEmail(input.email, filter); if (input.phone && H.isPhoneIdentifier()) await H.uniquePhone(input.phone, filter); if (input.whatsapp && H.isWhatsappIdentifier()) await H.uniqueWhatsapp(input.whatsapp, filter); if (input.username && H.isUsernameIdentifier()) await H.uniqueUsername(input.username, filter); } // {input, validFields, isCreate = false, fileFields = [], relations = [], request = {}} input = H.getValidFields({ input: input, validFields: self.modelFields, isCreate: false, relations: self.modelRelationFields, request: null, fileFields: self.fileFields, secondaryKey: self.secondaryKey, }).pop(); await self.auditTrail.setOldValues(context, isAdmin, model); result = await model.update(input); result.data = H.formatToVariables(result.data, self.secondaryKey, self.collection); self.auditTrail.record(input, 'update', result.data); } catch (error) { console.error(error); result = null; result = { error: error.message }; throw new Yekonga.Error.Forbidden(result.error); } if (model === null) throw new Yekonga.Error.Forbidden(getRejectedByAction({modelClass: self.modelClass, params, action: 'before find on update'})); var afterSaveInjectFunction = beforeAndAfterCallback('afterSave', self.modelClass, params, isAdmin)